dev_1.2
tangjie 2026-07-27 11:54:30 +08:00
parent f7ab71d7b8
commit 93a6c55b10
13 changed files with 559 additions and 11 deletions

View File

@ -0,0 +1,45 @@
{
"openapi": "3.0.1",
"info": { "title": "实时监控统计", "version": "1.0.0" },
"paths": {
"/safetyEval/regulator/eval-project-monitor/monitor-realtime-stat": {
"get": {
"summary": "实时监控统计",
"tags": ["监管端-安评项目"],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/SingleResponseRegulatorEvalProjectMonitorStatCO" }
}
}
}
}
}
}
},
"components": {
"schemas": {
"SingleResponseRegulatorEvalProjectMonitorStatCO": {
"type": "object",
"properties": {
"success": { "type": "boolean" },
"code": { "type": "string" },
"message": { "type": "string" },
"errMessage": { "type": "string" },
"data": { "$ref": "#/components/schemas/RegulatorEvalProjectMonitorStatCO" }
}
},
"RegulatorEvalProjectMonitorStatCO": {
"type": "object",
"properties": {
"monitoringCount": { "type": "integer", "description": "在监项目数" },
"normalCount": { "type": "integer", "description": "正常执行数(未归档)" },
"warningCount": { "type": "integer", "description": "进度/人员预警数" },
"monthPlanCompleteCount": { "type": "integer", "description": "本月计划完成数" }
}
}
}
}
}

View File

@ -0,0 +1,21 @@
// GET /safetyEval/regulator/eval-project-monitor/monitor-realtime-stat
// See ./spec.json
export interface RegulatorEvalProjectMonitorStatCO {
/** 在监项目数 */
monitoringCount: number;
/** 正常执行数(未归档) */
normalCount: number;
/** 进度/人员预警数 */
warningCount: number;
/** 本月计划完成数 */
monthPlanCompleteCount: number;
}
export interface SingleResponseRegulatorEvalProjectMonitorStatCO {
success: boolean;
code: string;
message: string;
errMessage: string;
data: RegulatorEvalProjectMonitorStatCO;
}

View File

@ -0,0 +1,70 @@
{
"openapi": "3.0.1",
"info": { "title": "实时监控分页", "version": "1.0.0" },
"paths": {
"/safetyEval/regulator/eval-project-monitor/realtime-page": {
"get": {
"summary": "实时监控分页",
"tags": ["监管端-安评项目"],
"parameters": [
{ "name": "current", "in": "query", "schema": { "type": "integer", "default": 1 } },
{ "name": "size", "in": "query", "schema": { "type": "integer", "default": 10 } },
{ "name": "projectNameOrNo", "in": "query", "schema": { "type": "string" }, "description": "项目名称/编号" },
{ "name": "evalTypeCode", "in": "query", "schema": { "type": "string" }, "description": "评价类型编码PRE/ACCEPT/STATUS" },
{ "name": "industryCode", "in": "query", "schema": { "type": "string" }, "description": "行业类别编码" },
{ "name": "leaderName", "in": "query", "schema": { "type": "string" }, "description": "项目负责人" },
{ "name": "planEndDateStart", "in": "query", "schema": { "type": "string" }, "description": "计划完成日期-起" },
{ "name": "planEndDateEnd", "in": "query", "schema": { "type": "string" }, "description": "计划完成日期-止" },
{ "name": "waringStatus", "in": "query", "schema": { "type": "string" }, "description": "监管状态normal正常PERSONNEL_ABNORMAL人员异常" }
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/PageResponseRegulatorEvalProjectMonitorPageCO" }
}
}
}
}
}
}
},
"components": {
"schemas": {
"PageResponseRegulatorEvalProjectMonitorPageCO": {
"type": "object",
"properties": {
"success": { "type": "boolean" },
"code": { "type": "string" },
"message": { "type": "string" },
"errMessage": { "type": "string" },
"data": {
"type": "array",
"items": { "$ref": "#/components/schemas/RegulatorEvalProjectMonitorPageCO" }
},
"total": { "type": "integer", "format": "int64" }
}
},
"RegulatorEvalProjectMonitorPageCO": {
"type": "object",
"properties": {
"id": { "type": "string", "description": "项目ID" },
"orgId": { "type": "string", "description": "机构ID" },
"projectNo": { "type": "string", "description": "项目编号" },
"projectName": { "type": "string", "description": "项目名称" },
"orgName": { "type": "string", "description": "评价机构" },
"evalTypeCode": { "type": "string", "description": "评价类型编码" },
"industryCode": { "type": "string", "description": "行业编码" },
"leaderName": { "type": "string", "description": "负责人" },
"planEndDate": { "type": "string", "description": "计划完成日期" },
"waringStatus": { "type": "string", "description": "预警状态normal正常abnormal异常" },
"processWaringStatus": { "type": "string", "description": "过程预警状态" },
"processWaringMsg": { "type": "string", "description": "过程预警信息" },
"personnelWaringStatus": { "type": "string", "description": "人员预警状态" },
"personnelWaringMsg": { "type": "string", "description": "人员预警信息" }
}
}
}
}
}

View File

@ -0,0 +1,29 @@
// GET /safetyEval/regulator/eval-project-monitor/realtime-page
// See ./spec.json
export interface RegulatorEvalProjectMonitorPageCO {
id: string;
orgId: string;
projectNo: string;
projectName: string;
orgName: string;
evalTypeCode: string;
industryCode: string;
leaderName: string;
planEndDate: string;
/** 预警状态normal正常abnormal异常 */
waringStatus: string;
processWaringStatus: string;
processWaringMsg: string;
personnelWaringStatus: string;
personnelWaringMsg: string;
}
export interface PageResponseRegulatorEvalProjectMonitorPageCO {
success: boolean;
code: string;
message: string;
errMessage: string;
data: RegulatorEvalProjectMonitorPageCO[];
total: number;
}

View File

@ -13,8 +13,8 @@ module.exports = {
//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
}, },
production: { production: {

View File

@ -250,4 +250,17 @@ export const evalProcessControlFiles = declareRequest(
"processControlFilesLoading", "processControlFilesLoading",
"Get > /safetyEval/institution/eval-process-control/process-files", "Get > /safetyEval/institution/eval-process-control/process-files",
"processControlFiles: [] | res.data || []", "processControlFiles: [] | res.data || []",
);
// ===== 监管端-安评项目实时监控 =====
export const evalProjectMonitorStat = declareRequest(
"evalProjectMonitorStatLoading",
"Get > /safetyEval/regulator/eval-project-monitor/monitor-realtime-stat",
"evalProjectMonitorStat: {} | res.data || {}",
);
export const evalProjectMonitorPage = declareRequest(
"evalProjectMonitorPageLoading",
"Get > /safetyEval/regulator/eval-project-monitor/realtime-page",
); );

View File

@ -399,6 +399,18 @@ export const NODE_LABELS = {
PROCESS_CONTROL: "过程控制", PROCESS_CONTROL: "过程控制",
}; };
/** 监管端-安评项目实时监控 - 监管状态映射 */
export const MONITOR_WARING_STATUS_MAP = {
normal: { color: "success", text: "正常" },
PERSONNEL_ABNORMAL: { color: "warning", text: "异常" },
};
/** 监管端-安评项目实时监控 - 监管状态筛选选项 */
export const MONITOR_WARING_STATUS_OPTIONS = [
{ label: "正常", value: "normal" },
{ label: "人员异常", value: "PERSONNEL_ABNORMAL" },
];
export const ROLE_DEFINITIONS = [ export const ROLE_DEFINITIONS = [
{ value: "MARKETING_BUSINESS_REP", name: "市场部业务员", unique: false, summary: "获取项目基础信息,形成风险分析记录;风险通过后签订合同并下达任务通知单。" }, { value: "MARKETING_BUSINESS_REP", name: "市场部业务员", unique: false, summary: "获取项目基础信息,形成风险分析记录;风险通过后签订合同并下达任务通知单。" },

View File

@ -195,6 +195,11 @@ const menuItems = [
label: "资质保持监控", label: "资质保持监控",
icon: <BarChartOutlined />, icon: <BarChartOutlined />,
}, },
{
key: "/safetyEval/container/MonitorManage/EvalProjectMonitor",
label: "安评项目实时监控",
icon: <FileTextOutlined />,
},
], ],
}, },
{ {

View File

@ -0,0 +1,271 @@
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>
),
}
];
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));

View File

@ -0,0 +1,3 @@
.epm-stat-row {
margin-bottom: 16px;
}

View File

@ -249,13 +249,53 @@ const EvalProjectCreate = (props) => {
</Row> </Row>
<Row gutter={24}> <Row gutter={24}>
<Col span={12}> <Col span={12}>
<Form.Item name="planStartDate" label="项目开始时间" rules={[{ required: true, message: "请选择项目开始时间" }]}> <Form.Item
<DatePicker style={{ width: "100%" }} placeholder="请选择" /> name="planStartDate"
label="项目开始时间"
rules={[
{ required: true, message: "请选择项目开始时间" },
{
validator: (_, value) => {
const end = form.getFieldValue("planEndDate");
if (value && end && value.isAfter(end)) {
return Promise.reject("项目开始时间不能晚于项目完成时间");
}
return Promise.resolve();
},
},
]}
>
<DatePicker
style={{ width: "100%" }}
placeholder="请选择"
allowClear
onChange={() => form.validateFields(["planEndDate"])}
/>
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item name="planEndDate" label="项目完成时间" rules={[{ required: true, message: "请选择项目完成时间" }]}> <Form.Item
<DatePicker style={{ width: "100%" }} placeholder="请选择" /> name="planEndDate"
label="项目完成时间"
rules={[
{ required: true, message: "请选择项目完成时间" },
{
validator: (_, value) => {
const start = form.getFieldValue("planStartDate");
if (value && start && value.isBefore(start)) {
return Promise.reject("项目完成时间不能早于项目开始时间");
}
return Promise.resolve();
},
},
]}
>
<DatePicker
style={{ width: "100%" }}
placeholder="请选择"
allowClear
onChange={() => form.validateFields(["planStartDate"])}
/>
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>

View File

@ -270,18 +270,57 @@ const ContractContent = (props) => {
<Form.Item <Form.Item
label="合同开始时间" label="合同开始时间"
name="contractStartDate" name="contractStartDate"
rules={[{ required: true, message: "请选择合同开始时间" }]} rules={[
{ required: true, message: "请选择合同开始时间" },
{
validator: (_, value) => {
const end = form.getFieldValue("contractEndDate");
if (value && end && value.isAfter(end)) {
return Promise.reject(
"合同开始时间不能晚于合同结束时间",
);
}
return Promise.resolve();
},
},
]}
> >
<DatePicker className="pf-w-full" allowClear /> <DatePicker
className="pf-w-full"
allowClear
onChange={() =>
form.validateFields(["contractEndDate"])
}
/>
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
label="合同结束时间" label="合同结束时间"
name="contractEndDate" name="contractEndDate"
rules={[{ required: true, message: "请选择合同结束时间" }]} rules={[
{ required: true, message: "请选择合同结束时间" },
{
validator: (_, value) => {
const start =
form.getFieldValue("contractStartDate");
if (value && start && value.isBefore(start)) {
return Promise.reject(
"合同结束时间不能早于合同开始时间",
);
}
return Promise.resolve();
},
},
]}
> >
<DatePicker className="pf-w-full" allowClear /> <DatePicker
className="pf-w-full"
allowClear
onChange={() =>
form.validateFields(["contractStartDate"])
}
/>
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>

View File

@ -228,7 +228,7 @@ const TeamContent = (props) => {
<Flex justify="flex-end" gap={8} className="pf-bottom-actions"> <Flex justify="flex-end" gap={8} className="pf-bottom-actions">
<Button>生成项目组任命文件</Button>
<Button type="primary" loading={memberSaveLoading} onClick={handleSave}>确认项目组</Button> <Button type="primary" loading={memberSaveLoading} onClick={handleSave}>确认项目组</Button>
</Flex> </Flex>