From 12b5d7b7a68c0e6fbd2a3b81b286e345e9887d42 Mon Sep 17 00:00:00 2001 From: tangjie <122778500@qq.com> Date: Mon, 27 Jul 2026 14:10:33 +0800 Subject: [PATCH] feat --- jjb.config.js | 4 +- src/pages/Container/Layout/menuConfig.js | 2 +- .../EvalProjectMonitor/Detail/index.js | 430 ++++++++++++++++++ .../EvalProjectMonitor/Detail/index.less | 45 ++ .../EvalProjectMonitor/List/index.js | 279 ++++++++++++ .../EvalProjectMonitor/{ => List}/index.less | 0 .../MonitorManage/EvalProjectMonitor/index.js | 269 +---------- 7 files changed, 762 insertions(+), 267 deletions(-) create mode 100644 src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js create mode 100644 src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.less create mode 100644 src/pages/Container/MonitorManage/EvalProjectMonitor/List/index.js rename src/pages/Container/MonitorManage/EvalProjectMonitor/{ => List}/index.less (100%) diff --git a/jjb.config.js b/jjb.config.js index 956c00a..9dcc37c 100644 --- a/jjb.config.js +++ b/jjb.config.js @@ -12,8 +12,8 @@ module.exports = { // 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095 //API_HOST: "https://gbs-gateway.qhdsafety.com", - //API_HOST: "http://192.168.0.152", - API_HOST: "http://192.168.0.150", //太浅 + API_HOST: "http://192.168.0.152", + //API_HOST: "http://192.168.0.150", //太浅 // API_HOST: "http://192.168.0.152", //API_HOST: "http://192.168.0.103", //huwei }, diff --git a/src/pages/Container/Layout/menuConfig.js b/src/pages/Container/Layout/menuConfig.js index d4fea86..2dea24e 100644 --- a/src/pages/Container/Layout/menuConfig.js +++ b/src/pages/Container/Layout/menuConfig.js @@ -196,7 +196,7 @@ const menuItems = [ icon: , }, { - key: "/safetyEval/container/MonitorManage/EvalProjectMonitor", + key: "/safetyEval/container/MonitorManage/EvalProjectMonitor/List", label: "安评项目实时监控", icon: , }, diff --git a/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js b/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js new file mode 100644 index 0000000..b97c9b4 --- /dev/null +++ b/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js @@ -0,0 +1,430 @@ +import React, { useState, useEffect } from "react"; +import { + Tabs, + Descriptions, + Table, + Tag, + Card, + Spin, + Empty, + Typography, +} from "antd"; +import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; +import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd"; +import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace"; +import { tools } from "@cqsjjb/jjb-common-lib"; +import { EVAL_TYPE_MAP, ROLE_DEFINITIONS_MAP } from "~/enumerate/constant"; +import { QUALIFICATION_INDUSTRY_OPTIONS_MAP } from "~/enumerate/enterpriseOptions"; +import "./index.less"; + +const { router } = tools; + +const PROJECT_TYPE_MAP = { + NEW: "新建项目", + RENEW: "续评项目", + CHANGE: "变更项目", +}; + +const YES_NO_MAP = { 1: "是", 2: "否" }; + +const ATTACH_TYPE_LABELS = { + PRACTICE_NOTIFICATION: "从业告知书", + SITE_INSPECTION_FORM: "现场安全检查表", + RECTIFICATION_NOTICE: "整改通知单", + ENTERPRISE_RECTIFICATION: "企业整改报告", + RECTIFICATION_REVIEW: "整改复查单", +}; + +/** 解析 JSON 格式的文件列表 */ +const parseFileList = (raw) => { + if (!raw) return []; + try { + const parsed = typeof raw === "string" ? JSON.parse(raw) : raw; + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +}; + +// ===== 合同面板 ===== +const ContractPanel = ({ data, loading }) => { + if (!data?.id) return ; + const scanFiles = parseFileList(data?.scanFileUrl); + + return ( +
+ + + {data.contractNo || "-"} + + + {data.amount ?? "-"} + + + {data.contractStartDate || "-"} ~ {data.contractEndDate || "-"} + + + {EVAL_TYPE_MAP[data.evalTypeCode] || data.evalTypeCode || "-"} + + + {data.projectName || "-"} + + + {PROJECT_TYPE_MAP[data.projectTypeCode] || + data.projectTypeCode || + "-"} + + + {data.businessScope || "-"} + + + {data.projectAddress || "-"} + + + {data.projectIntro || "-"} + + + + + + {data.enterpriseName || "-"} + + + {data.creditCode || "-"} + + + {data.legalPerson || "-"} + + + {data.contactName || "-"} + + + {data.contactPhone || "-"} + + + {data.officeAddress || "-"} + + + + {scanFiles.length > 0 && ( + + {scanFiles.map((file, idx) => ( + + {file.name || `附件${idx + 1}`} + + ))} + + )} +
+ ); +}; + +// ===== 项目组人员面板 ===== +const TeamPanel = ({ data, loading }) => { + + if (!data || data.length === 0) + return ; + + const columns = [ + { title: "姓名", dataIndex: "personnelName", width: 100 }, + { title: "部门/岗位", dataIndex: "deptName", width: 160, ellipsis: true }, + { + title: "项目角色", + dataIndex: "role", + width: 200, + render: (roles) => + (Array.isArray(roles) ? roles : [roles]) + .map((r) => ROLE_DEFINITIONS_MAP[r] || r) + .join("、"), + }, + { + title: "资质与专业", + dataIndex: "capacity", + width: 200, + ellipsis: true, + }, + { + title: "主要职责", + dataIndex: "responsibility", + ellipsis: true, + }, + ]; + + return ( + + ); +}; + +// ===== 踏勘情况面板 ===== +const SurveyPanel = ({ personnel, attachments, loading, detailData }) => { + if (loading) return ; + + const personnelColumns = [ + { title: "人员", dataIndex: "personnelName", width: 100 }, + { + title: "项目角色", + dataIndex: "role", + width: 160, + ellipsis: true, + render: (v) => + Array.isArray(v) + ? v.map((r) => ROLE_DEFINITIONS_MAP[r] || r).join("、") + : v || "-", + }, + { + title: "人脸识别", + dataIndex: "faceState", + width: 90, + render: (v) => ( + + {v === 1 ? "通过" : "未通过"} + + ), + }, + { + title: "GPS定位", + dataIndex: "gpsState", + width: 90, + render: (v) => ( + + {v === 1 ? "正常" : "异常"} + + ), + }, + { title: "签到时间", dataIndex: "singInTime", width: 140 }, + { title: "签退时间", dataIndex: "signOutTime", width: 140 }, + { + title: "现场照片", + dataIndex: "sceneUrl", + width: 80, + render: (v) => { + const count = v ? v.split(",").filter(Boolean).length : 0; + return count > 0 ? `${count}张` : "-"; + }, + }, + { + title: "本人签字", + dataIndex: "signatureUrl", + width: 90, + render: (v) => ( + {v ? "已签字" : "未签字"} + ), + }, + ]; + + return ( +
+ {detailData && ( +
+ 打卡企业地址:{detailData?.customerAddress || "-"} + + 允许打卡范围:企业地址周边 + {detailData?.checkinRadiusMeters || "-"}米 + +
+ )} + + + {personnel && personnel.length > 0 ? ( +
+ ) : ( + + )} + + + {Object.keys(ATTACH_TYPE_LABELS).map((typeCode) => { + const files = parseFileList(attachments[typeCode]); + if (files.length === 0) return null; + return ( + + {files.map((file, idx) => ( + + {file.name || `附件${idx + 1}`} + + ))} + + ); + })} + + ); +}; + +// ===== 主页面 ===== +const EvalProjectMonitorDetail = (props) => { + const projectId = router.query?.id; + + const { + safetyEvalBusiness, + evalContractGet, + evalProjectMemberPage, + evalSurveyList, + evalSurveyPage, + evalProjectDetail, + } = props; + const { + contractLoading, + memberLoading, + evalSurveyListLoading, + evalSurveyPageLoading, + evalProjectDetailData, + } = safetyEvalBusiness || {}; + + const [contractData, setContractData] = useState(null); + const [teamMembers, setTeamMembers] = useState([]); + const [surveyPersonnel, setSurveyPersonnel] = useState([]); + const [surveyAttachments, setSurveyAttachments] = useState({}); + const [activeKey, setActiveKey] = useState("contract"); + + const fetchContract = async () => { + if (!projectId) return; + const res = await evalContractGet({ projectId }); + if (res?.success !== false && res?.data) { + setContractData(res.data); + } + }; + + const fetchTeam = async () => { + if (!projectId) return; + const res = await evalProjectMemberPage({ projectId }); + if (res?.success !== false && res?.data) { + setTeamMembers(res.data); + } + }; + + const fetchSurvey = async () => { + if (!projectId) return; + const [listRes, pageRes] = await Promise.all([ + evalSurveyList({ projectId }), + evalSurveyPage({ projectId, size: 100 }), + ]); + if (listRes?.success !== false) { + const map = {}; + (listRes?.data || []).forEach((item) => { + map[item.attachTypeCode] = item.fileUrl; + }); + setSurveyAttachments(map); + } + if (pageRes?.success !== false) { + setSurveyPersonnel(pageRes?.data || []); + } + }; + + useEffect(() => { + if (projectId) { + evalProjectDetail({ id: projectId }); + } + fetchContract(); + fetchTeam(); + fetchSurvey(); + }, []); + + const projectInfo = ( +
+
+ 项目名称 + + {evalProjectDetailData?.projectName || + contractData?.projectName || + "-"} + +
+
+ 项目编号 + {evalProjectDetailData?.projectNo || "-"} +
+
+ 评价机构 + + {contractData?.orgName || evalProjectDetailData?.orgName || "-"} + +
+
+ 项目负责人 + {evalProjectDetailData?.leaderName || "-"} +
+
+ ); + + return ( + + {projectInfo} + + ), + }, + { + key: "team", + label: "项目组人员", + children: , + }, + { + key: "survey", + label: "踏勘情况", + children: ( + + ), + }, + ]} + /> + + ); +}; + +export default Connect( + [NS_SAFETY_EVAL_BUSINESS], + true, +)(AntdTableFuncControl(EvalProjectMonitorDetail)); diff --git a/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.less b/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.less new file mode 100644 index 0000000..f799ad7 --- /dev/null +++ b/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.less @@ -0,0 +1,45 @@ +.epm-detail-band { + display: flex; + gap: 32px; + padding: 12px 16px; + background: #fafafa; + border-radius: 6px; + margin-bottom: 16px; + border: 1px solid #f0f0f0; + + .epm-detail-band-item { + display: flex; + flex-direction: column; + gap: 4px; + + span { + font-size: 12px; + color: #8c8c8c; + } + + strong { + font-size: 14px; + color: #262626; + } + } +} + +.epm-detail-panel { + .epm-detail-section { + margin-top: 16px; + } + + .epm-detail-info-bar { + display: flex; + gap: 24px; + margin-bottom: 12px; + font-size: 13px; + color: #6b7280; + } + + .epm-detail-file { + display: inline-block; + margin-right: 12px; + margin-bottom: 4px; + } +} diff --git a/src/pages/Container/MonitorManage/EvalProjectMonitor/List/index.js b/src/pages/Container/MonitorManage/EvalProjectMonitor/List/index.js new file mode 100644 index 0000000..98f7aa1 --- /dev/null +++ b/src/pages/Container/MonitorManage/EvalProjectMonitor/List/index.js @@ -0,0 +1,279 @@ +import React, { useState, useEffect } from "react"; +import { + Row, + Col, + Card, + Statistic, + Table, + Tag, + Button, + Form, + Space, + Tooltip, +} from "antd"; +import dayjs from "dayjs"; +import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd"; +import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; +import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm"; +import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper"; +import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace"; +import { tools } from "@cqsjjb/jjb-common-lib"; +import { + EVAL_TYPE_OPTIONS, + EVAL_TYPE_MAP, + MONITOR_WARING_STATUS_OPTIONS, +} from "~/enumerate/constant"; +import { + QUALIFICATION_INDUSTRY_OPTIONS, + QUALIFICATION_INDUSTRY_OPTIONS_MAP, +} from "~/enumerate/enterpriseOptions"; +import "./index.less"; + +const { router } = tools; + +const STAT_ITEMS = [ + { key: "monitoringCount", title: "在监项目", color: "#1677ff" }, + { key: "normalCount", title: "正常执行", color: "#52c41a" }, + { key: "warningCount", title: "进度/人员预警", color: "#faad14" }, + { key: "monthPlanCompleteCount", title: "本月计划完成", color: "#722ed1" }, +]; + +const EvalProjectMonitor = (props) => { + const [searchForm] = Form.useForm(); + const [dataSource, setDataSource] = useState([]); + const [total, setTotal] = useState(0); + + const { safetyEvalBusiness, evalProjectMonitorStat, evalProjectMonitorPage } = + props; + const { evalProjectMonitorStatLoading, evalProjectMonitorPageLoading } = + safetyEvalBusiness || {}; + const statData = safetyEvalBusiness?.evalProjectMonitorStat || {}; + + const getStat = async () => { + evalProjectMonitorStat(); + }; + + const getData = async () => { + const params = { + ...router.query, + current: router.query.current || 1, + size: router.query.size || 10, + }; + const res = await evalProjectMonitorPage(params); + if (res?.success !== false) { + setDataSource(res?.data || []); + setTotal(res?.total || 0); + } + }; + + useEffect(() => { + searchForm.setFieldsValue({ + ...router.query, + planEndDateRange: + router.query.planEndDateStart && router.query.planEndDateEnd + ? [ + dayjs(router.query.planEndDateStart), + dayjs(router.query.planEndDateEnd), + ] + : undefined, + }); + getStat(); + getData(); + }, []); + + const handleSearch = (values) => { + const { planEndDateRange, ...rest } = values; + router.query = { + ...rest, + planEndDateStart: + planEndDateRange?.[0]?.format?.("YYYY-MM-DD") || undefined, + planEndDateEnd: + planEndDateRange?.[1]?.format?.("YYYY-MM-DD") || undefined, + current: 1, + size: 10, + }; + getData(); + }; + + const handleReset = (values) => { + searchForm.resetFields(); + values.planEndDateStart = undefined; + values.planEndDateEnd = undefined; + router.query = { ...values, current: 1, size: 10 }; + getData(); + }; + + const handlePageChange = (pagination) => { + router.query = { + ...router.query, + current: pagination.current, + size: pagination.pageSize, + }; + getData(); + }; + + const renderWaringStatus = (_, record) => { + if (record.waringStatus === "NORMAL") { + return 正常; + } + const tags = []; + if (record.processWaringMsg) { + tags.push( + + 进度预警 + , + ); + } + if (record.personnelWaringMsg) { + tags.push( + + 人员异常 + , + ); + } + + return {tags}; + }; + + const columns = [ + { title: "项目编号", dataIndex: "projectNo", width: 140 }, + { title: "项目名称", dataIndex: "projectName", ellipsis: true, width: 220 }, + { title: "评价机构", dataIndex: "orgName", ellipsis: true, width: 200 }, + { + title: "评价类型", + dataIndex: "evalTypeCode", + width: 120, + render: (code) => EVAL_TYPE_MAP[code] || code, + }, + { + title: "行业", + dataIndex: "industryCode", + width: 140, + ellipsis: true, + render: (code) => QUALIFICATION_INDUSTRY_OPTIONS_MAP[code] || code, + }, + { title: "负责人", dataIndex: "leaderName", width: 90 }, + { title: "计划完成", dataIndex: "planEndDate", width: 120 }, + { + title: "状态", + width: 100, + render: renderWaringStatus, + }, + { + title: "操作", + width: 120, + fixed: "right", + render: (_, record) => ( + + ), + }, + ]; + + return ( + + + {STAT_ITEMS.map((item) => ( +
+ + + + + ))} + + + + + , + + + , + + + , + + + , + + + , + + + , + ]} + onFinish={handleSearch} + onReset={handleReset} + /> + +
`共 ${t} 条`, + }} + onChange={handlePageChange} + /> + + ); +}; + +export default Connect( + [NS_SAFETY_EVAL_BUSINESS], + true, +)(AntdTableFuncControl(EvalProjectMonitor)); diff --git a/src/pages/Container/MonitorManage/EvalProjectMonitor/index.less b/src/pages/Container/MonitorManage/EvalProjectMonitor/List/index.less similarity index 100% rename from src/pages/Container/MonitorManage/EvalProjectMonitor/index.less rename to src/pages/Container/MonitorManage/EvalProjectMonitor/List/index.less diff --git a/src/pages/Container/MonitorManage/EvalProjectMonitor/index.js b/src/pages/Container/MonitorManage/EvalProjectMonitor/index.js index d65d9b4..f568631 100644 --- a/src/pages/Container/MonitorManage/EvalProjectMonitor/index.js +++ b/src/pages/Container/MonitorManage/EvalProjectMonitor/index.js @@ -1,271 +1,12 @@ -import React, { useState, useEffect } from "react"; -import { - Row, - Col, - Card, - Statistic, - Table, - Tag, - Form, - Space, - Tooltip, -} from "antd"; -import dayjs from "dayjs"; -import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd"; -import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; -import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm"; -import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper"; -import { Connect } from "@cqsjjb/jjb-dva-runtime"; -import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace"; -import { tools } from "@cqsjjb/jjb-common-lib"; -import { - EVAL_TYPE_OPTIONS, - EVAL_TYPE_MAP, - MONITOR_WARING_STATUS_OPTIONS, -} from "~/enumerate/constant"; -import { - QUALIFICATION_INDUSTRY_OPTIONS, - QUALIFICATION_INDUSTRY_OPTIONS_MAP, -} from "~/enumerate/enterpriseOptions"; -import "./index.less"; -const { router } = tools; - -const STAT_ITEMS = [ - { key: "monitoringCount", title: "在监项目", color: "#1677ff" }, - { key: "normalCount", title: "正常执行", color: "#52c41a" }, - { key: "warningCount", title: "进度/人员预警", color: "#faad14" }, - { key: "monthPlanCompleteCount", title: "本月计划完成", color: "#722ed1" }, -]; - -const EvalProjectMonitor = (props) => { - const [searchForm] = Form.useForm(); - const [dataSource, setDataSource] = useState([]); - const [total, setTotal] = useState(0); - - const { safetyEvalBusiness, evalProjectMonitorStat, evalProjectMonitorPage } = - props; - const { evalProjectMonitorStatLoading, evalProjectMonitorPageLoading } = - safetyEvalBusiness || {}; - const statData = safetyEvalBusiness?.evalProjectMonitorStat || {}; - - const getStat = async () => { - evalProjectMonitorStat(); - }; - - const getData = async () => { - const params = { - ...router.query, - current: router.query.current || 1, - size: router.query.size || 10, - }; - const res = await evalProjectMonitorPage(params); - if (res?.success !== false) { - setDataSource(res?.data || []); - setTotal(res?.total || 0); - } - }; - - useEffect(() => { - searchForm.setFieldsValue({ - ...router.query, - planEndDateRange: - router.query.planEndDateStart && router.query.planEndDateEnd - ? [ - dayjs(router.query.planEndDateStart), - dayjs(router.query.planEndDateEnd), - ] - : undefined, - }); - getStat(); - getData(); - }, []); - - const handleSearch = (values) => { - const { planEndDateRange, ...rest } = values; - router.query = { - ...rest, - planEndDateStart: - planEndDateRange?.[0]?.format?.("YYYY-MM-DD") || undefined, - planEndDateEnd: - planEndDateRange?.[1]?.format?.("YYYY-MM-DD") || undefined, - current: 1, - size: 10, - }; - getData(); - }; - - const handleReset = (values) => { - searchForm.resetFields(); - values.planEndDateStart = undefined; - values.planEndDateEnd = undefined; - router.query = { ...values, current: 1, size: 10 }; - getData(); - }; - - const handlePageChange = (pagination) => { - router.query = { - ...router.query, - current: pagination.current, - size: pagination.pageSize, - }; - getData(); - }; - - const renderWaringStatus = (_, record) => { - if (record.waringStatus === "NORMAL") { - return 正常; - } - const tags = []; - if (record.processWaringMsg) { - tags.push( - - 进度预警 - , - ); - } - if (record.personnelWaringMsg) { - tags.push( - - 人员异常 - , - ); - } - - return {tags}; - }; - - const columns = [ - { title: "项目编号", dataIndex: "projectNo", width: 140 }, - { title: "项目名称", dataIndex: "projectName", ellipsis: true, width: 220 }, - { title: "评价机构", dataIndex: "orgName", ellipsis: true, width: 200 }, - { - title: "评价类型", - dataIndex: "evalTypeCode", - width: 120, - render: (code) => EVAL_TYPE_MAP[code] || code, - }, - { - title: "行业", - dataIndex: "industryCode", - width: 140, - ellipsis: true, - render: (code) => QUALIFICATION_INDUSTRY_OPTIONS_MAP[code] || code, - }, - { title: "负责人", dataIndex: "leaderName", width: 90 }, - { title: "计划完成", dataIndex: "planEndDate", width: 120 }, - { - title: "状态", - width: 100, - render: renderWaringStatus, - }, - { - title: "操作", - width: 180, - fixed: "right", - render: (_, record) => ( - - - - ), - } - ]; +import React from "react"; +function MonitorManage(props) { return ( - - - {STAT_ITEMS.map((item) => ( - - - - - - ))} - - - - , - - - , - - - , - - - , - - - , - - - , - ]} - onFinish={handleSearch} - onReset={handleReset} - /> + props.children -
`共 ${t} 条`, - }} - onChange={handlePageChange} - /> - ); -}; +} -export default Connect( - [NS_SAFETY_EVAL_BUSINESS], - true, -)(AntdTableFuncControl(EvalProjectMonitor)); +export default MonitorManage;