feat
parent
93a6c55b10
commit
12b5d7b7a6
|
|
@ -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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ const menuItems = [
|
|||
icon: <BarChartOutlined />,
|
||||
},
|
||||
{
|
||||
key: "/safetyEval/container/MonitorManage/EvalProjectMonitor",
|
||||
key: "/safetyEval/container/MonitorManage/EvalProjectMonitor/List",
|
||||
label: "安评项目实时监控",
|
||||
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;
|
||||
|
||||
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>
|
||||
),
|
||||
}
|
||||
];
|
||||
import React from "react";
|
||||
|
||||
function MonitorManage(props) {
|
||||
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}
|
||||
/>
|
||||
props.children
|
||||
|
||||
<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));
|
||||
export default MonitorManage;
|
||||
|
|
|
|||
Loading…
Reference in New Issue