dev-tmp1
luotaiqian 2026-08-14 16:58:32 +08:00
parent 892d154b03
commit bdec681206
4 changed files with 206 additions and 87 deletions

View File

@ -101,14 +101,21 @@ export const evalProjectProgressPage = declareRequest(
"Get > /safetyEval/institution/eval-project/progress-page",
);
export const evalProjectRiskWarningStat = declareRequest(
// ===== 机构端-险预警 =====
export const evalProjectOrgWaringStat = declareRequest(
"riskWarningStatLoading",
"Get > /safetyEval/institution/eval-project/risk-warning-stat",
"Get > /safetyEval/eval-project-org-waring/org/stat",
);
export const evalProjectRiskWarningPage = declareRequest(
export const evalProjectOrgWaringPage = declareRequest(
"riskWarningPageLoading",
"Get > /safetyEval/institution/eval-project/risk-warning-page",
"Get > /safetyEval/eval-project-org-waring/list/org/waring/page",
);
export const evalProjectOrgWaringHandle = declareRequest(
"riskWarningHandleLoading",
"Post > @/safetyEval/eval-project-org-waring/handle",
);
export const evalRiskAnalysisGet = declareRequest(

View File

@ -390,17 +390,26 @@ export const MATERIAL_TYPE_MAP = MATERIAL_TYPE_OPTIONS.reduce((acc, cur) => {
return acc;
}, {});
/** 风险预警 — 风险等级映射 */
export const RISK_LEVEL_MAP = {
HIGH: { color: "error", text: "高风险" },
MIDDLE: { color: "warning", text: "中风险" },
LOW: { color: "default", text: "低风险" },
/** 机构端-险预警 — 预警类型映射 */
export const ORG_WARNING_TYPE_MAP = {
not_archived: { color: "warning", text: "未归档" },
report_overdue: { color: "error", text: "报告超期" },
CORPORATE_RECTIFICATION: { color: "processing", text: "企业整改" },
MAJOR_HAZARD: { color: "error", text: "重大隐患" },
};
/** 风险预警 — 处理状态映射 */
export const RISK_STATUS_MAP = {
UNHANDLED: { color: "error", text: "未处理" },
PROCESSING: { color: "warning", text: "处理中" },
/** 机构端-险预警 — 预警类型筛选选项 */
export const ORG_WARNING_TYPE_OPTIONS = [
{ label: "未归档", value: "not_archived" },
{ label: "报告超期", value: "report_overdue" },
{ label: "企业整改", value: "CORPORATE_RECTIFICATION" },
{ label: "重大隐患", value: "MAJOR_HAZARD" },
];
/** 机构端-险预警 — 处理状态映射 */
export const ORG_WARNING_HANDLE_STATUS_MAP = {
none: { color: "error", text: "未处理" },
done: { color: "success", text: "已处理" },
};
/** 项目进度 — 进度状态映射 */

View File

@ -2,22 +2,29 @@ import React, { useState, useEffect } from "react";
import {
Form,
Table,
Button,
Tag,
Modal,
Row,
Col,
Card,
Statistic,
Space,
Button,
Modal,
Upload,
Flex,
Input,
message,
} from "antd";
import { UploadOutlined } from "@ant-design/icons";
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "~/components/SearchForm";
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
import { RISK_LEVEL_MAP, RISK_STATUS_MAP } from "~/enumerate/constant";
import {
ORG_WARNING_TYPE_MAP,
ORG_WARNING_TYPE_OPTIONS,
ORG_WARNING_HANDLE_STATUS_MAP,
} from "~/enumerate/constant";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { tools } from "@cqsjjb/jjb-common-lib";
import "./index.less";
@ -29,12 +36,18 @@ const RiskWarning = (props) => {
const [dataSource, setDataSource] = useState([]);
const [total, setTotal] = useState(0);
const [statData, setStatData] = useState({});
const [handleModalOpen, setHandleModalOpen] = useState(false);
const [currentRecord, setCurrentRecord] = useState(null);
const [modalForm] = Form.useForm();
const [uploading, setUploading] = useState(false);
const { riskWarningPageLoading } = props.safetyEvalBusiness;
const {
riskWarningPageLoading,
riskWarningHandleLoading,
} = props.safetyEvalBusiness;
const getStat = async () => {
const res = await props.evalProjectRiskWarningStat();
const res = await props.evalProjectOrgWaringStat();
if (res?.success !== false) {
setStatData(res?.data || {});
}
@ -46,7 +59,7 @@ const RiskWarning = (props) => {
current: router.query.current || 1,
size: router.query.size || 10,
};
const res = await props.evalProjectRiskWarningPage(params);
const res = await props.evalProjectOrgWaringPage(params);
if (res?.success !== false) {
setDataSource(res?.data || []);
setTotal(res?.total || 0);
@ -79,10 +92,8 @@ const RiskWarning = (props) => {
getData();
};
const columns = [
{ title: "项目编号", dataIndex: "projectNo", width: 130 },
{ title: "项目编号", dataIndex: "projectNo", width: 140 },
{
title: "项目/客户",
width: 240,
@ -94,39 +105,75 @@ const RiskWarning = (props) => {
),
},
{
title: "风险等级",
dataIndex: "riskLevelCode",
width: 100,
title: "预警类型",
dataIndex: "warningType",
width: 110,
render: (code) => {
const cfg = RISK_LEVEL_MAP[code];
return cfg ? <Tag color={cfg.color}>{cfg.text}</Tag> : "";
const cfg = ORG_WARNING_TYPE_MAP[code];
return cfg ? <Tag color={cfg.color}>{cfg.text}</Tag> : code || "";
},
},
{ title: "预警类型", dataIndex: "warningTypeName", width: 100 },
{
title: "触发条件",
dataIndex: "triggerCondition",
ellipsis: true,
width: 260,
},
{ title: "人", dataIndex: "responsiblePerson", width: 90 },
{ title: "预警时间", dataIndex: "warningTime", width: 120 },
{ title: "业务负责人", dataIndex: "projectLeaderName", width: 110 },
{ title: "创建时间", dataIndex: "createTime", width: 170 },
{
title: "状态",
dataIndex: "statusCode",
width: 90,
title: "处理状态",
dataIndex: "handleStatus",
width: 100,
render: (code) => {
const cfg = RISK_STATUS_MAP[code];
return cfg ? <Tag color={cfg.color}>{cfg.text}</Tag> : "";
const cfg = ORG_WARNING_HANDLE_STATUS_MAP[code];
return cfg ? <Tag color={cfg.color}>{cfg.text}</Tag> : code || "";
},
},
{
title: "操作",
width: 90,
fixed: "right",
render: (_, record) =>
record.handleStatus === "none" ? (
<Button
type="link"
size="small"
onClick={() => {
setCurrentRecord(record);
setHandleModalOpen(true);
}}
>
处理
</Button>
) : null,
},
];
const handleModalOk = async () => {
const values = await modalForm.validateFields();
const res = await props.evalProjectOrgWaringHandle({
id: currentRecord?.id,
...values,
});
if (res?.success !== false) {
message.success("处理成功");
setHandleModalOpen(false);
modalForm.resetFields();
getStat();
getData();
}
};
const handleModalCancel = () => {
setHandleModalOpen(false);
modalForm.resetFields();
};
return (
<PageLayout title="风险预警管理">
<Row gutter={16} className="rw-stat-row">
<Col span={6}>
<Col span={12}>
<Card size="small">
<Statistic
title="预警总数"
@ -135,27 +182,7 @@ const RiskWarning = (props) => {
/>
</Card>
</Col>
<Col span={6}>
<Card size="small">
<Statistic
title="高风险"
value={statData.highRiskCount ?? "-"}
valueStyle={{ color: "#ff4d4f" }}
suffix={<span className="rw-stat-suffix">延期逾期重大整改</span>}
/>
</Card>
</Col>
<Col span={6}>
<Card size="small">
<Statistic
title="处理中"
value={statData.processingCount ?? "-"}
valueStyle={{ color: "#faad14" }}
suffix={<span className="rw-stat-suffix">已分派但未闭环</span>}
/>
</Card>
</Col>
<Col span={6}>
<Col span={12}>
<Card size="small">
<Statistic
title="本周新增"
@ -167,44 +194,24 @@ const RiskWarning = (props) => {
</Col>
</Row>
<SearchForm
form={searchForm}
loading={false}
style={{ marginBottom: 16 }}
formLine={[
<Form.Item key="projectOrCustomer" name="projectOrCustomer">
<Form.Item key="keyword" name="keyword">
<ControlWrapper.Input
label="项目/客户"
placeholder="项目或客户名称"
allowClear
/>
</Form.Item>,
<Form.Item key="riskLevel" name="riskLevel">
<ControlWrapper.Select
label="风险等级"
placeholder="全部"
allowClear
options={[
{ label: "高风险", value: "HIGH" },
{ label: "中风险", value: "MIDDLE" },
{ label: "低风险", value: "LOW" },
]}
/>
</Form.Item>,
<Form.Item key="warningType" name="warningType">
<ControlWrapper.Select
label="预警类型"
placeholder="全部"
allowClear
options={[
{ label: "进度风险", value: "PROGRESS" },
{ label: "周期风险", value: "CYCLE" },
{ label: "质量风险", value: "QUALITY" },
{ label: "人员风险", value: "PERSONNEL" },
{ label: "归档风险", value: "ARCHIVE" },
]}
options={ORG_WARNING_TYPE_OPTIONS}
/>
</Form.Item>,
]}
@ -212,14 +219,12 @@ const RiskWarning = (props) => {
onReset={handleReset}
/>
<Table
rowKey="id"
columns={columns}
dataSource={dataSource}
loading={riskWarningPageLoading}
scroll={{ y: props.scrollY, x: 1300 }}
scroll={{ y: props.scrollY, x: 1130 }}
pagination={{
total,
current: router.query.current || 1,
@ -231,7 +236,49 @@ const RiskWarning = (props) => {
onChange={handlePageChange}
/>
<Modal
title="处理预警"
open={handleModalOpen}
onOk={handleModalOk}
onCancel={handleModalCancel}
confirmLoading={riskWarningHandleLoading}
destroyOnClose
>
<Form form={modalForm} layout="vertical">
<Form.Item label="项目编号">
<span className="rw-modal-project-no">
{currentRecord?.projectNo}
</span>
</Form.Item>
<Form.Item
label="证明材料"
name="evidenceFileUrl"
rules={[
{ required: true, message: "请上传证明材料" },
]}
>
<UploadButtonField
uploading={uploading}
setUploading={setUploading}
/>
</Form.Item>
<Form.Item
label="备注处理说明"
name="remarks"
rules={[
{ required: true, message: "请输入备注处理说明" },
{ max: 255, message: "备注处理说明不能超过255个字符" },
]}
>
<Input.TextArea
placeholder="请输入处理说明"
maxLength={255}
showCount
rows={4}
/>
</Form.Item>
</Form>
</Modal>
</PageLayout>
);
};
@ -239,4 +286,47 @@ const RiskWarning = (props) => {
export default Connect(
[NS_SAFETY_EVAL_BUSINESS],
true,
)(AntdTableFuncControl(RiskWarning));
)(AntdTableFuncControl(RiskWarning));
/** 证明材料上传字段 - 上传后回填 url */
const UploadButtonField = ({ value, onChange, uploading, setUploading }) => {
const fileName =
typeof value === "string" && value.includes("/")
? value.split("/").pop()
: value;
return (
<Flex gap={8} align="center">
{fileName && (
<span className="rw-file-name" title={fileName}>
{fileName}
</span>
)}
<Upload
showUploadList={false}
action={`${window.process.env.app.API_HOST}/safetyEval/file/upload`}
headers={{ token: sessionStorage.getItem("token") }}
onChange={(info) => {
if (info.file.status === "uploading") {
setUploading(true);
} else if (info.file.status === "done") {
setUploading(false);
const url = info.file.response?.data?.url;
if (url) {
onChange?.(url);
} else {
message.error("上传失败");
}
} else if (info.file.status === "error") {
setUploading(false);
message.error("上传失败");
}
}}
>
<Button icon={<UploadOutlined />} loading={uploading} size="small">
选择文件
</Button>
</Upload>
</Flex>
);
};

View File

@ -26,4 +26,17 @@
font-size: 12px;
color: #999;
}
&-file-name {
max-width: 160px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
color: #999;
}
&-modal-project-no {
font-weight: 500;
}
}