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", "Get > /safetyEval/institution/eval-project/progress-page",
); );
export const evalProjectRiskWarningStat = declareRequest( // ===== 机构端-险预警 =====
export const evalProjectOrgWaringStat = declareRequest(
"riskWarningStatLoading", "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", "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( export const evalRiskAnalysisGet = declareRequest(

View File

@ -390,17 +390,26 @@ export const MATERIAL_TYPE_MAP = MATERIAL_TYPE_OPTIONS.reduce((acc, cur) => {
return acc; return acc;
}, {}); }, {});
/** 风险预警 — 风险等级映射 */ /** 机构端-险预警 — 预警类型映射 */
export const RISK_LEVEL_MAP = { export const ORG_WARNING_TYPE_MAP = {
HIGH: { color: "error", text: "高风险" }, not_archived: { color: "warning", text: "未归档" },
MIDDLE: { color: "warning", text: "中风险" }, report_overdue: { color: "error", text: "报告超期" },
LOW: { color: "default", text: "低风险" }, CORPORATE_RECTIFICATION: { color: "processing", text: "企业整改" },
MAJOR_HAZARD: { color: "error", text: "重大隐患" },
}; };
/** 风险预警 — 处理状态映射 */ /** 机构端-险预警 — 预警类型筛选选项 */
export const RISK_STATUS_MAP = { export const ORG_WARNING_TYPE_OPTIONS = [
UNHANDLED: { color: "error", text: "未处理" }, { label: "未归档", value: "not_archived" },
PROCESSING: { color: "warning", text: "处理中" }, { 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 { import {
Form, Form,
Table, Table,
Button,
Tag, Tag,
Modal,
Row, Row,
Col, Col,
Card, Card,
Statistic, Statistic,
Space, Button,
Modal,
Upload,
Flex,
Input,
message, message,
} from "antd"; } from "antd";
import { UploadOutlined } from "@ant-design/icons";
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd"; import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "~/components/SearchForm"; import SearchForm from "~/components/SearchForm";
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper"; import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace"; 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 { Connect } from "@cqsjjb/jjb-dva-runtime";
import { tools } from "@cqsjjb/jjb-common-lib"; import { tools } from "@cqsjjb/jjb-common-lib";
import "./index.less"; import "./index.less";
@ -29,12 +36,18 @@ const RiskWarning = (props) => {
const [dataSource, setDataSource] = useState([]); const [dataSource, setDataSource] = useState([]);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [statData, setStatData] = useState({}); const [statData, setStatData] = useState({});
const [handleModalOpen, setHandleModalOpen] = useState(false);
const [currentRecord, setCurrentRecord] = useState(null);
const [modalForm] = Form.useForm();
const [uploading, setUploading] = useState(false);
const {
const { riskWarningPageLoading } = props.safetyEvalBusiness; riskWarningPageLoading,
riskWarningHandleLoading,
} = props.safetyEvalBusiness;
const getStat = async () => { const getStat = async () => {
const res = await props.evalProjectRiskWarningStat(); const res = await props.evalProjectOrgWaringStat();
if (res?.success !== false) { if (res?.success !== false) {
setStatData(res?.data || {}); setStatData(res?.data || {});
} }
@ -46,7 +59,7 @@ const RiskWarning = (props) => {
current: router.query.current || 1, current: router.query.current || 1,
size: router.query.size || 10, size: router.query.size || 10,
}; };
const res = await props.evalProjectRiskWarningPage(params); const res = await props.evalProjectOrgWaringPage(params);
if (res?.success !== false) { if (res?.success !== false) {
setDataSource(res?.data || []); setDataSource(res?.data || []);
setTotal(res?.total || 0); setTotal(res?.total || 0);
@ -79,10 +92,8 @@ const RiskWarning = (props) => {
getData(); getData();
}; };
const columns = [ const columns = [
{ title: "项目编号", dataIndex: "projectNo", width: 130 }, { title: "项目编号", dataIndex: "projectNo", width: 140 },
{ {
title: "项目/客户", title: "项目/客户",
width: 240, width: 240,
@ -94,39 +105,75 @@ const RiskWarning = (props) => {
), ),
}, },
{ {
title: "风险等级", title: "预警类型",
dataIndex: "riskLevelCode", dataIndex: "warningType",
width: 100, width: 110,
render: (code) => { render: (code) => {
const cfg = RISK_LEVEL_MAP[code]; const cfg = ORG_WARNING_TYPE_MAP[code];
return cfg ? <Tag color={cfg.color}>{cfg.text}</Tag> : ""; return cfg ? <Tag color={cfg.color}>{cfg.text}</Tag> : code || "";
}, },
}, },
{ title: "预警类型", dataIndex: "warningTypeName", width: 100 },
{ {
title: "触发条件", title: "触发条件",
dataIndex: "triggerCondition", dataIndex: "triggerCondition",
ellipsis: true, ellipsis: true,
width: 260, width: 260,
}, },
{ title: "人", dataIndex: "responsiblePerson", width: 90 }, { title: "业务负责人", dataIndex: "projectLeaderName", width: 110 },
{ title: "预警时间", dataIndex: "warningTime", width: 120 }, { title: "创建时间", dataIndex: "createTime", width: 170 },
{ {
title: "状态", title: "处理状态",
dataIndex: "statusCode", dataIndex: "handleStatus",
width: 90, width: 100,
render: (code) => { render: (code) => {
const cfg = RISK_STATUS_MAP[code]; const cfg = ORG_WARNING_HANDLE_STATUS_MAP[code];
return cfg ? <Tag color={cfg.color}>{cfg.text}</Tag> : ""; 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 ( return (
<PageLayout title="风险预警管理"> <PageLayout title="风险预警管理">
<Row gutter={16} className="rw-stat-row"> <Row gutter={16} className="rw-stat-row">
<Col span={6}> <Col span={12}>
<Card size="small"> <Card size="small">
<Statistic <Statistic
title="预警总数" title="预警总数"
@ -135,27 +182,7 @@ const RiskWarning = (props) => {
/> />
</Card> </Card>
</Col> </Col>
<Col span={6}> <Col span={12}>
<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}>
<Card size="small"> <Card size="small">
<Statistic <Statistic
title="本周新增" title="本周新增"
@ -167,44 +194,24 @@ const RiskWarning = (props) => {
</Col> </Col>
</Row> </Row>
<SearchForm <SearchForm
form={searchForm} form={searchForm}
loading={false} loading={false}
style={{ marginBottom: 16 }} style={{ marginBottom: 16 }}
formLine={[ formLine={[
<Form.Item key="projectOrCustomer" name="projectOrCustomer"> <Form.Item key="keyword" name="keyword">
<ControlWrapper.Input <ControlWrapper.Input
label="项目/客户" label="项目/客户"
placeholder="项目或客户名称" placeholder="项目或客户名称"
allowClear allowClear
/> />
</Form.Item>, </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"> <Form.Item key="warningType" name="warningType">
<ControlWrapper.Select <ControlWrapper.Select
label="预警类型" label="预警类型"
placeholder="全部" placeholder="全部"
allowClear allowClear
options={[ options={ORG_WARNING_TYPE_OPTIONS}
{ label: "进度风险", value: "PROGRESS" },
{ label: "周期风险", value: "CYCLE" },
{ label: "质量风险", value: "QUALITY" },
{ label: "人员风险", value: "PERSONNEL" },
{ label: "归档风险", value: "ARCHIVE" },
]}
/> />
</Form.Item>, </Form.Item>,
]} ]}
@ -212,14 +219,12 @@ const RiskWarning = (props) => {
onReset={handleReset} onReset={handleReset}
/> />
<Table <Table
rowKey="id" rowKey="id"
columns={columns} columns={columns}
dataSource={dataSource} dataSource={dataSource}
loading={riskWarningPageLoading} loading={riskWarningPageLoading}
scroll={{ y: props.scrollY, x: 1300 }} scroll={{ y: props.scrollY, x: 1130 }}
pagination={{ pagination={{
total, total,
current: router.query.current || 1, current: router.query.current || 1,
@ -231,7 +236,49 @@ const RiskWarning = (props) => {
onChange={handlePageChange} 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> </PageLayout>
); );
}; };
@ -240,3 +287,46 @@ export default Connect(
[NS_SAFETY_EVAL_BUSINESS], [NS_SAFETY_EVAL_BUSINESS],
true, 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; font-size: 12px;
color: #999; 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;
}
} }