feat
parent
93a6c55b10
commit
12b5d7b7a6
|
|
@ -12,8 +12,8 @@ module.exports = {
|
||||||
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
|
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
|
||||||
//API_HOST: "https://gbs-gateway.qhdsafety.com",
|
//API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||||
|
|
||||||
//API_HOST: "http://192.168.0.152",
|
API_HOST: "http://192.168.0.152",
|
||||||
API_HOST: "http://192.168.0.150", //太浅
|
//API_HOST: "http://192.168.0.150", //太浅
|
||||||
// API_HOST: "http://192.168.0.152",
|
// API_HOST: "http://192.168.0.152",
|
||||||
//API_HOST: "http://192.168.0.103", //huwei
|
//API_HOST: "http://192.168.0.103", //huwei
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ const menuItems = [
|
||||||
icon: <BarChartOutlined />,
|
icon: <BarChartOutlined />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "/safetyEval/container/MonitorManage/EvalProjectMonitor",
|
key: "/safetyEval/container/MonitorManage/EvalProjectMonitor/List",
|
||||||
label: "安评项目实时监控",
|
label: "安评项目实时监控",
|
||||||
icon: <FileTextOutlined />,
|
icon: <FileTextOutlined />,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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 <Spin><Empty description="暂无合同数据" /></Spin>;
|
||||||
|
const scanFiles = parseFileList(data?.scanFileUrl);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="epm-detail-panel">
|
||||||
|
<Descriptions title="合同基本信息" bordered column={2} size="small">
|
||||||
|
<Descriptions.Item label="合同编号">
|
||||||
|
{data.contractNo || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="合同金额(万元)">
|
||||||
|
{data.amount ?? "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="合同期限">
|
||||||
|
{data.contractStartDate || "-"} ~ {data.contractEndDate || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="评价类型">
|
||||||
|
{EVAL_TYPE_MAP[data.evalTypeCode] || data.evalTypeCode || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="项目名称" span={2}>
|
||||||
|
{data.projectName || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="项目类型">
|
||||||
|
{PROJECT_TYPE_MAP[data.projectTypeCode] ||
|
||||||
|
data.projectTypeCode ||
|
||||||
|
"-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="项目业务范围">
|
||||||
|
{data.businessScope || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="项目详细地址" span={2}>
|
||||||
|
{data.projectAddress || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="项目简介" span={2}>
|
||||||
|
{data.projectIntro || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
|
||||||
|
<Descriptions
|
||||||
|
title="被评价企业信息"
|
||||||
|
bordered
|
||||||
|
column={2}
|
||||||
|
size="small"
|
||||||
|
className="epm-detail-section"
|
||||||
|
>
|
||||||
|
<Descriptions.Item label="企业名称">
|
||||||
|
{data.enterpriseName || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="统一信用代码">
|
||||||
|
{data.creditCode || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="法人代表">
|
||||||
|
{data.legalPerson || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="联系人">
|
||||||
|
{data.contactName || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="联系电话">
|
||||||
|
{data.contactPhone || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="办公地址">
|
||||||
|
{data.officeAddress || "-"}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
|
||||||
|
{scanFiles.length > 0 && (
|
||||||
|
<Card
|
||||||
|
type="inner"
|
||||||
|
size="small"
|
||||||
|
title="合同扫描件"
|
||||||
|
className="epm-detail-section"
|
||||||
|
>
|
||||||
|
{scanFiles.map((file, idx) => (
|
||||||
|
<Typography.Link
|
||||||
|
key={file.uid || idx}
|
||||||
|
href={file.url}
|
||||||
|
target="_blank"
|
||||||
|
className="epm-detail-file"
|
||||||
|
>
|
||||||
|
{file.name || `附件${idx + 1}`}
|
||||||
|
</Typography.Link>
|
||||||
|
))}
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 项目组人员面板 =====
|
||||||
|
const TeamPanel = ({ data, loading }) => {
|
||||||
|
|
||||||
|
if (!data || data.length === 0)
|
||||||
|
return <Empty description="暂无项目组人员数据" />;
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<Table
|
||||||
|
dataSource={data}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="personnelId"
|
||||||
|
pagination={false}
|
||||||
|
size="small"
|
||||||
|
scroll={{ x: 800 }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 踏勘情况面板 =====
|
||||||
|
const SurveyPanel = ({ personnel, attachments, loading, detailData }) => {
|
||||||
|
if (loading) return <Empty />;
|
||||||
|
|
||||||
|
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) => (
|
||||||
|
<Tag color={v === 1 ? "success" : "error"}>
|
||||||
|
{v === 1 ? "通过" : "未通过"}
|
||||||
|
</Tag>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "GPS定位",
|
||||||
|
dataIndex: "gpsState",
|
||||||
|
width: 90,
|
||||||
|
render: (v) => (
|
||||||
|
<Tag color={v === 1 ? "success" : "error"}>
|
||||||
|
{v === 1 ? "正常" : "异常"}
|
||||||
|
</Tag>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ 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) => (
|
||||||
|
<Tag color={v ? "success" : "default"}>{v ? "已签字" : "未签字"}</Tag>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="epm-detail-panel">
|
||||||
|
{detailData && (
|
||||||
|
<div className="epm-detail-info-bar">
|
||||||
|
<span>打卡企业地址:{detailData?.customerAddress || "-"}</span>
|
||||||
|
<span>
|
||||||
|
允许打卡范围:企业地址周边
|
||||||
|
{detailData?.checkinRadiusMeters || "-"}米
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Card type="inner" size="small" title="人员现场打卡情况">
|
||||||
|
{personnel && personnel.length > 0 ? (
|
||||||
|
<Table
|
||||||
|
dataSource={personnel}
|
||||||
|
columns={personnelColumns}
|
||||||
|
rowKey="id"
|
||||||
|
pagination={false}
|
||||||
|
size="small"
|
||||||
|
scroll={{ x: 900 }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Empty description="暂无打卡记录" />
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{Object.keys(ATTACH_TYPE_LABELS).map((typeCode) => {
|
||||||
|
const files = parseFileList(attachments[typeCode]);
|
||||||
|
if (files.length === 0) return null;
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
key={typeCode}
|
||||||
|
type="inner"
|
||||||
|
size="small"
|
||||||
|
title={ATTACH_TYPE_LABELS[typeCode]}
|
||||||
|
className="epm-detail-section"
|
||||||
|
>
|
||||||
|
{files.map((file, idx) => (
|
||||||
|
<Typography.Link
|
||||||
|
key={file.uid || idx}
|
||||||
|
href={file.url}
|
||||||
|
target="_blank"
|
||||||
|
className="epm-detail-file"
|
||||||
|
>
|
||||||
|
{file.name || `附件${idx + 1}`}
|
||||||
|
</Typography.Link>
|
||||||
|
))}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 主页面 =====
|
||||||
|
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 = (
|
||||||
|
<div className="epm-detail-band">
|
||||||
|
<div className="epm-detail-band-item">
|
||||||
|
<span>项目名称</span>
|
||||||
|
<strong>
|
||||||
|
{evalProjectDetailData?.projectName ||
|
||||||
|
contractData?.projectName ||
|
||||||
|
"-"}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
<div className="epm-detail-band-item">
|
||||||
|
<span>项目编号</span>
|
||||||
|
<strong>{evalProjectDetailData?.projectNo || "-"}</strong>
|
||||||
|
</div>
|
||||||
|
<div className="epm-detail-band-item">
|
||||||
|
<span>评价机构</span>
|
||||||
|
<strong>
|
||||||
|
{contractData?.orgName || evalProjectDetailData?.orgName || "-"}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
<div className="epm-detail-band-item">
|
||||||
|
<span>项目负责人</span>
|
||||||
|
<strong>{evalProjectDetailData?.leaderName || "-"}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageLayout history={props.history} previous title="安评项目监管详情">
|
||||||
|
{projectInfo}
|
||||||
|
<Tabs
|
||||||
|
activeKey={activeKey}
|
||||||
|
onChange={setActiveKey}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: "contract",
|
||||||
|
label: "项目合同",
|
||||||
|
children: (
|
||||||
|
<ContractPanel data={contractData} loading={contractLoading} />
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "team",
|
||||||
|
label: "项目组人员",
|
||||||
|
children: <TeamPanel data={teamMembers} loading={memberLoading} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "survey",
|
||||||
|
label: "踏勘情况",
|
||||||
|
children: (
|
||||||
|
<SurveyPanel
|
||||||
|
personnel={surveyPersonnel}
|
||||||
|
attachments={surveyAttachments}
|
||||||
|
loading={evalSurveyListLoading || evalSurveyPageLoading}
|
||||||
|
detailData={safetyEvalBusiness?.evalProjectDetailData}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</PageLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Connect(
|
||||||
|
[NS_SAFETY_EVAL_BUSINESS],
|
||||||
|
true,
|
||||||
|
)(AntdTableFuncControl(EvalProjectMonitorDetail));
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 <Tag color="success">正常</Tag>;
|
||||||
|
}
|
||||||
|
const tags = [];
|
||||||
|
if (record.processWaringMsg) {
|
||||||
|
tags.push(
|
||||||
|
<Tooltip key="process" title={record.processWaringMsg}>
|
||||||
|
<Tag color="warning">进度预警</Tag>
|
||||||
|
</Tooltip>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (record.personnelWaringMsg) {
|
||||||
|
tags.push(
|
||||||
|
<Tooltip key="personnel" title={record.personnelWaringMsg}>
|
||||||
|
<Tag color="error">人员异常</Tag>
|
||||||
|
</Tooltip>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Space size={4}>{tags}</Space>;
|
||||||
|
};
|
||||||
|
|
||||||
|
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) => (
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
|
||||||
|
onClick={() => {
|
||||||
|
props.history.push(
|
||||||
|
`Detail?id=${record.id}`,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
查看过程
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageLayout title="安评项目实时监控">
|
||||||
|
<Row gutter={16} className="epm-stat-row">
|
||||||
|
{STAT_ITEMS.map((item) => (
|
||||||
|
<Col span={6} key={item.key}>
|
||||||
|
<Card size="small">
|
||||||
|
<Statistic
|
||||||
|
title={item.title}
|
||||||
|
value={statData[item.key] ?? "-"}
|
||||||
|
valueStyle={{ color: item.color }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<SearchForm
|
||||||
|
form={searchForm}
|
||||||
|
loading={false}
|
||||||
|
style={{ marginBottom: 16 }}
|
||||||
|
formLine={[
|
||||||
|
<Form.Item key="projectNameOrNo" name="projectNameOrNo">
|
||||||
|
<ControlWrapper.Input
|
||||||
|
label="项目名称/编号"
|
||||||
|
placeholder="请输入"
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="evalTypeCode" name="evalTypeCode">
|
||||||
|
<ControlWrapper.Select
|
||||||
|
label="评价类型"
|
||||||
|
placeholder="全部"
|
||||||
|
allowClear
|
||||||
|
options={EVAL_TYPE_OPTIONS}
|
||||||
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="industryCode" name="industryCode">
|
||||||
|
<ControlWrapper.Select
|
||||||
|
label="行业类别"
|
||||||
|
placeholder="全部"
|
||||||
|
allowClear
|
||||||
|
options={QUALIFICATION_INDUSTRY_OPTIONS}
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="leaderName" name="leaderName">
|
||||||
|
<ControlWrapper.Input
|
||||||
|
label="项目负责人"
|
||||||
|
placeholder="请输入"
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="waringStatus" name="waringStatus">
|
||||||
|
<ControlWrapper.Select
|
||||||
|
label="监管状态"
|
||||||
|
placeholder="全部"
|
||||||
|
allowClear
|
||||||
|
options={MONITOR_WARING_STATUS_OPTIONS}
|
||||||
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="planEndDateRange" name="planEndDateRange">
|
||||||
|
<ControlWrapper.DatePicker.RangePicker
|
||||||
|
label="计划完成时间"
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
]}
|
||||||
|
onFinish={handleSearch}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
rowKey="id"
|
||||||
|
columns={columns}
|
||||||
|
dataSource={dataSource}
|
||||||
|
loading={evalProjectMonitorPageLoading}
|
||||||
|
scroll={{ y: props.scrollY, x: 1200 }}
|
||||||
|
pagination={{
|
||||||
|
total,
|
||||||
|
current: Number(router.query.current) || 1,
|
||||||
|
pageSize: Number(router.query.size) || 10,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (t) => `共 ${t} 条`,
|
||||||
|
}}
|
||||||
|
onChange={handlePageChange}
|
||||||
|
/>
|
||||||
|
</PageLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Connect(
|
||||||
|
[NS_SAFETY_EVAL_BUSINESS],
|
||||||
|
true,
|
||||||
|
)(AntdTableFuncControl(EvalProjectMonitor));
|
||||||
|
|
@ -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;
|
import React from "react";
|
||||||
|
|
||||||
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 <Tag color="success">正常</Tag>;
|
|
||||||
}
|
|
||||||
const tags = [];
|
|
||||||
if (record.processWaringMsg) {
|
|
||||||
tags.push(
|
|
||||||
<Tooltip key="process" title={record.processWaringMsg}>
|
|
||||||
<Tag color="warning">进度预警</Tag>
|
|
||||||
</Tooltip>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (record.personnelWaringMsg) {
|
|
||||||
tags.push(
|
|
||||||
<Tooltip key="personnel" title={record.personnelWaringMsg}>
|
|
||||||
<Tag color="error">人员异常</Tag>
|
|
||||||
</Tooltip>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Space size={4}>{tags}</Space>;
|
|
||||||
};
|
|
||||||
|
|
||||||
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) => (
|
|
||||||
<Space size={4}>
|
|
||||||
<Button type="primary" onClick={()=>{}}>
|
|
||||||
查看过程
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
),
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
|
function MonitorManage(props) {
|
||||||
return (
|
return (
|
||||||
<PageLayout title="安评项目实时监控">
|
|
||||||
<Row gutter={16} className="epm-stat-row">
|
|
||||||
{STAT_ITEMS.map((item) => (
|
|
||||||
<Col span={6} key={item.key}>
|
|
||||||
<Card size="small">
|
|
||||||
<Statistic
|
|
||||||
title={item.title}
|
|
||||||
value={statData[item.key] ?? "-"}
|
|
||||||
valueStyle={{ color: item.color }}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
|
|
||||||
<SearchForm
|
props.children
|
||||||
form={searchForm}
|
|
||||||
loading={false}
|
|
||||||
style={{ marginBottom: 16 }}
|
|
||||||
formLine={[
|
|
||||||
<Form.Item key="projectNameOrNo" name="projectNameOrNo">
|
|
||||||
<ControlWrapper.Input
|
|
||||||
label="项目名称/编号"
|
|
||||||
placeholder="请输入"
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
</Form.Item>,
|
|
||||||
<Form.Item key="evalTypeCode" name="evalTypeCode">
|
|
||||||
<ControlWrapper.Select
|
|
||||||
label="评价类型"
|
|
||||||
placeholder="全部"
|
|
||||||
allowClear
|
|
||||||
options={EVAL_TYPE_OPTIONS}
|
|
||||||
/>
|
|
||||||
</Form.Item>,
|
|
||||||
<Form.Item key="industryCode" name="industryCode">
|
|
||||||
<ControlWrapper.Select
|
|
||||||
label="行业类别"
|
|
||||||
placeholder="全部"
|
|
||||||
allowClear
|
|
||||||
options={QUALIFICATION_INDUSTRY_OPTIONS}
|
|
||||||
showSearch
|
|
||||||
optionFilterProp="label"
|
|
||||||
/>
|
|
||||||
</Form.Item>,
|
|
||||||
<Form.Item key="leaderName" name="leaderName">
|
|
||||||
<ControlWrapper.Input
|
|
||||||
label="项目负责人"
|
|
||||||
placeholder="请输入"
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
</Form.Item>,
|
|
||||||
<Form.Item key="waringStatus" name="waringStatus">
|
|
||||||
<ControlWrapper.Select
|
|
||||||
label="监管状态"
|
|
||||||
placeholder="全部"
|
|
||||||
allowClear
|
|
||||||
options={MONITOR_WARING_STATUS_OPTIONS}
|
|
||||||
/>
|
|
||||||
</Form.Item>,
|
|
||||||
<Form.Item key="planEndDateRange" name="planEndDateRange">
|
|
||||||
<ControlWrapper.DatePicker.RangePicker
|
|
||||||
label="计划完成时间"
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
/>
|
|
||||||
</Form.Item>,
|
|
||||||
]}
|
|
||||||
onFinish={handleSearch}
|
|
||||||
onReset={handleReset}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Table
|
|
||||||
rowKey="id"
|
|
||||||
columns={columns}
|
|
||||||
dataSource={dataSource}
|
|
||||||
loading={evalProjectMonitorPageLoading}
|
|
||||||
scroll={{ y: props.scrollY, x: 1200 }}
|
|
||||||
pagination={{
|
|
||||||
total,
|
|
||||||
current: Number(router.query.current) || 1,
|
|
||||||
pageSize: Number(router.query.size) || 10,
|
|
||||||
showSizeChanger: true,
|
|
||||||
showQuickJumper: true,
|
|
||||||
showTotal: (t) => `共 ${t} 条`,
|
|
||||||
}}
|
|
||||||
onChange={handlePageChange}
|
|
||||||
/>
|
|
||||||
</PageLayout>
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Connect(
|
export default MonitorManage;
|
||||||
[NS_SAFETY_EVAL_BUSINESS],
|
|
||||||
true,
|
|
||||||
)(AntdTableFuncControl(EvalProjectMonitor));
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue