safety-eval-service-frontend/src/pages/Container/MonitorManage/RiskCenter/index.js

367 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useState, useEffect } from "react";
import { Button, Form, Table, Tag } from "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 { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
import { tools } from "@cqsjjb/jjb-common-lib";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_RISK_CENTER } from "~/enumerate/namespace";
import "./index.less";
const { router } = tools;
/** 预警大类配置 */
const RISK_TYPE_MAP = {
qual: { label: "资质预警", color: "error" },
proj: { label: "项目预警", color: "warning" },
};
/** 处理阶段配置 */
const STAGE_MAP = {
untreated: { label: "未处理", color: "error" },
sent: { label: "已发机构", color: "warning" },
review: { label: "待复核", color: "processing" },
closed: { label: "已闭环", color: "success" },
};
/** 机构端状态配置 */
const ORG_STATUS_MAP = {
unsent: { label: "未发送", color: "error" },
unread: { label: "机构未读", color: "warning" },
processing: { label: "处理中", color: "processing" },
submitted: { label: "已提交材料", color: "processing" },
done: { label: "整改完成", color: "success" },
};
/** 预警子类选项 */
const RISK_SUBTYPE_OPTIONS = [
{ label: "资质条件保持异常", value: "qual_condition" },
{ label: "人员比例异常", value: "qual_person_rate" },
{ label: "属地材料待核验", value: "qual_area" },
{ label: "项目启动超期", value: "proj_start" },
{ label: "关键节点超期", value: "proj_node" },
{ label: "项目人员异常", value: "proj_person" },
{ label: "从业告知异常", value: "proj_notify" },
{ label: "现场勘查异常", value: "proj_site" },
{ label: "过程控制/归档异常", value: "proj_archive" },
];
/** 项目预警规则提示 */
const PROJ_RULES = [
"项目启动超期",
"关键节点超期",
"项目人员异常",
"从业告知异常",
"现场勘查异常",
"过程控制/归档异常",
];
/** mock 数据 */
const MOCK_DATA = [
{
id: "W-QUAL-2026-012",
type: "qual",
subtype: "qual_condition",
target: "重庆渝安风险评估中心",
sources: ["财务材料.固定资产", "场所核验.建筑面积", "人员库.专职人数"],
content: "固定资产、场所面积、档案室面积和专职人员数量不满足保持条件。",
stage: "untreated",
orgStatus: "unsent",
},
{
id: "W-QUAL-2026-011",
type: "qual",
subtype: "qual_person_rate",
target: "重庆恒安安全评价有限公司",
sources: ["人员库.职称等级", "人员库.注册安全工程师"],
content: "高级职称比例 26%,合计比例 59%,低于资质保持条件。",
stage: "sent",
orgStatus: "unread",
},
{
id: "W-QUAL-2026-013",
type: "qual",
subtype: "qual_area",
target: "北京中安评价中心重庆分公司",
sources: ["本市社保.缴纳人数", "场所核验.租赁合同"],
content: "机构已提交本地场所和人员说明,等待监管复核。",
stage: "review",
orgStatus: "submitted",
},
{
id: "W-PROJ-2026-021",
type: "proj",
subtype: "proj_start",
target: "华安化工年度安全现状评价",
sources: ["客户信息.评价到期日", "项目库.新建时间"],
content: "评价周期已到期 14 天,系统内尚未建立新一轮评价项目。",
stage: "untreated",
orgStatus: "unsent",
},
{
id: "W-PROJ-2026-018",
type: "proj",
subtype: "proj_node",
target: "安环检测年度安全现状评价",
sources: ["项目计划.节点期限", "流程记录.当前节点"],
content: "项目停留在过程控制环节 12 天,已超过计划完成时间。",
stage: "sent",
orgStatus: "processing",
},
{
id: "W-PROJ-2026-015",
type: "proj",
subtype: "proj_person",
target: "鼎信建设安全验收评价",
sources: ["项目组.项目负责人", "人员库.在职状态"],
content: "项目负责人已提交离职申请,项目尚未完成负责人变更。",
stage: "review",
orgStatus: "submitted",
},
{
id: "W-PROJ-2026-009",
type: "proj",
subtype: "proj_site",
target: "华安化工园区安全预评价",
sources: ["现场勘查.签到签退", "项目组.参与人员"],
content: "现场勘查仅有签到记录,无签退记录和现场照片。",
stage: "closed",
orgStatus: "done",
},
];
/** 统计卡片样式主题 */
const CARD_THEMES = [
{ bg: "#fee2e2", color: "#dc2626" },
{ bg: "#fef3c7", color: "#d97706" },
{ bg: "#dbeafe", color: "#2563eb" },
{ bg: "#d1fae5", color: "#059669" },
];
const RiskCenter = (props) => {
const [form] = Form.useForm();
const [data] = useState(MOCK_DATA);
const { riskCenter, queryRiskOverviewAction } = props;
const { overviewStats = [], riskOverviewLoading } = riskCenter || {};
useEffect(() => {
queryRiskOverviewAction();
}, []);
const handleSearch = () => {
// 接口搜索
};
const handleReset = () => {
form.resetFields();
};
const columns = [
{
title: "预警编号",
dataIndex: "id",
width: 160,
},
{
title: "预警大类",
dataIndex: "type",
width: 100,
render: (type) => {
const cfg = RISK_TYPE_MAP[type];
return cfg ? <Tag color={cfg.color}>{cfg.label}</Tag> : "--";
},
},
{
title: "预警子类",
dataIndex: "subtype",
width: 140,
render: (subtype) => {
const opt = RISK_SUBTYPE_OPTIONS.find((o) => o.value === subtype);
return opt?.label || "--";
},
},
{
title: "风险对象",
dataIndex: "target",
width: 200,
},
{
title: "来源字段",
dataIndex: "sources",
width: 280,
render: (sources) => (
<div style={{ display: "flex", flexWrap: "wrap", gap: 4 }}>
{(sources || []).map((s, i) => (
<span key={i} className="source-pill">
{s}
</span>
))}
</div>
),
},
{
title: "触发内容",
dataIndex: "content",
width: 320,
},
{
title: "处理阶段",
dataIndex: "stage",
width: 100,
render: (stage) => {
const cfg = STAGE_MAP[stage];
return cfg ? <Tag color={cfg.color}>{cfg.label}</Tag> : "--";
},
},
{
title: "机构端状态",
dataIndex: "orgStatus",
width: 110,
render: (status) => {
const cfg = ORG_STATUS_MAP[status];
return cfg ? <Tag color={cfg.color}>{cfg.label}</Tag> : "--";
},
},
{
title: "监管操作",
width: 100,
fixed: "right",
render: () => (
<Button type="primary" size="small">
处理
</Button>
),
},
];
return (
<PageLayout title="风险预警中心">
<div className="risk-center">
{/* 统计卡片 */}
<div className="stat-grid cols-4">
{overviewStats.map((stat, idx) => {
const theme = CARD_THEMES[idx % CARD_THEMES.length];
return (
<div className="stat-card" key={idx}>
<div className="stat-info">
<div className="stat-label">{stat.label}</div>
<div className="stat-value">{stat.value}</div>
<div className="stat-hint">{stat.hint}</div>
</div>
<div
className="stat-icon"
style={{ background: theme.bg, color: theme.color }}
>
{stat.label.charAt(0)}
</div>
</div>
);
})}
</div>
{/* 项目预警规则提示 */}
<div className="source-note">
<strong>项目预警规则</strong>
{PROJ_RULES.map((rule) => (
<span key={rule} className="source-pill">
{rule}
</span>
))}
</div>
{/* 搜索表单 */}
<SearchForm
style={{ marginBottom: 12 }}
form={form}
formLine={[
<Form.Item key="keyword" name="keyword">
<ControlWrapper.Input
label="关键词"
placeholder="机构/项目/人员/预警编号"
allowClear
/>
</Form.Item>,
<Form.Item key="riskType" name="riskType">
<ControlWrapper.Select
label="预警大类"
placeholder="全部"
allowClear
style={{ width: "100%" }}
options={[
{ label: "资质预警", value: "qual" },
{ label: "项目预警", value: "proj" },
]}
/>
</Form.Item>,
<Form.Item key="subtype" name="subtype">
<ControlWrapper.Select
label="预警子类"
placeholder="全部"
allowClear
style={{ width: "100%" }}
options={RISK_SUBTYPE_OPTIONS}
/>
</Form.Item>,
<Form.Item key="stage" name="stage">
<ControlWrapper.Select
label="处理阶段"
placeholder="全部"
allowClear
style={{ width: "100%" }}
options={[
{ label: "未处理", value: "untreated" },
{ label: "已发机构", value: "sent" },
{ label: "待复核", value: "review" },
{ label: "已闭环", value: "closed" },
]}
/>
</Form.Item>,
]}
onReset={handleReset}
onFinish={handleSearch}
/>
{/* 工具条 */}
<div className="risk-toolbar">
<div className="risk-toolbar-left">
<span className="risk-toolbar-desc">
资质预警沿用资质保持监控结果项目预警由项目计划人员从业告知现场记录和审核归档数据触发
</span>
</div>
<div className="risk-toolbar-right">
<Button className="btn-ghost" size="small">
导出预警
</Button>
</div>
</div>
{/* 数据表格 */}
<Table
rowKey="id"
columns={columns}
dataSource={data}
scroll={{ x: 1600, y: props.scrollY }}
pagination={{
total: data.length,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `${total}`,
current: router.query.current || 1,
pageSize: router.query.size || 10,
onChange: (page, size) => {
router.query = { ...router.query, current: page, size };
},
}}
/>
</div>
</PageLayout>
);
};
export default Connect(
[NS_RISK_CENTER],
true,
)(AntdTableFuncControl(RiskCenter));