feat
parent
201055a49b
commit
2d20215e96
|
|
@ -133,4 +133,16 @@ export const evalProjectMemberPage = declareRequest(
|
||||||
export const evalProjectMemberSave = declareRequest(
|
export const evalProjectMemberSave = declareRequest(
|
||||||
"memberSaveLoading",
|
"memberSaveLoading",
|
||||||
"Post > @/safetyEval/institution/eval-project-member/save",
|
"Post > @/safetyEval/institution/eval-project-member/save",
|
||||||
|
);
|
||||||
|
|
||||||
|
// ===== 项目预警(安评业务提醒) =====
|
||||||
|
|
||||||
|
export const projectWaringStat = declareRequest(
|
||||||
|
"projectWaringStatLoading",
|
||||||
|
"Get > /safetyEval/project-waring/stat",
|
||||||
|
);
|
||||||
|
|
||||||
|
export const projectWaringRemindPage = declareRequest(
|
||||||
|
"projectWaringRemindPageLoading",
|
||||||
|
"Get > /safetyEval/project-waring/remind-page",
|
||||||
);
|
);
|
||||||
|
|
@ -351,4 +351,22 @@ export const PROGRESS_STATUS_MAP = {
|
||||||
NORMAL: { color: "success", text: "正常" },
|
NORMAL: { color: "success", text: "正常" },
|
||||||
NEAR: { color: "warning", text: "项目临期" },
|
NEAR: { color: "warning", text: "项目临期" },
|
||||||
DELAY: { color: "error", text: "项目延期" },
|
DELAY: { color: "error", text: "项目延期" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 安评业务提醒 — 提醒等级映射 */
|
||||||
|
export const WARING_STATUS_MAP = {
|
||||||
|
first: { color: "success", text: "首次预警" },
|
||||||
|
second: { color: "warning", text: "二次提醒" },
|
||||||
|
urgent: { color: "error", text: "紧急预警" },
|
||||||
|
overdue: { color: "default", text: "已逾期" },
|
||||||
|
normal: { color: "default", text: "正常" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 安评业务提醒 — 提醒等级筛选选项 */
|
||||||
|
export const WARING_STATUS_OPTIONS = [
|
||||||
|
{ label: "首次预警(6个月)", value: "first" },
|
||||||
|
{ label: "二次提醒(3个月)", value: "second" },
|
||||||
|
{ label: "紧急预警(1个月)", value: "urgent" },
|
||||||
|
{ label: "已逾期", value: "overdue" },
|
||||||
|
{ label: "正常", value: "normal" },
|
||||||
|
];
|
||||||
|
|
@ -21,6 +21,7 @@ import {
|
||||||
FormOutlined,
|
FormOutlined,
|
||||||
CheckCircleOutlined,
|
CheckCircleOutlined,
|
||||||
SyncOutlined,
|
SyncOutlined,
|
||||||
|
BellOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
|
|
@ -236,6 +237,11 @@ const menuItems = [
|
||||||
label: "安评客户管理",
|
label: "安评客户管理",
|
||||||
icon: <TeamOutlined />,
|
icon: <TeamOutlined />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "/safetyEval/container/SafetyEvalBusiness/BusinessReminder",
|
||||||
|
label: "安评业务提醒",
|
||||||
|
icon: <BellOutlined />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,258 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
Table,
|
||||||
|
Button,
|
||||||
|
Tag,
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
Card,
|
||||||
|
Statistic,
|
||||||
|
Space,
|
||||||
|
} from "antd";
|
||||||
|
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 { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
|
||||||
|
import { WARING_STATUS_MAP, WARING_STATUS_OPTIONS } from "~/enumerate/constant";
|
||||||
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
|
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||||
|
import "./index.less";
|
||||||
|
|
||||||
|
const { router } = tools;
|
||||||
|
|
||||||
|
const INDUSTRY_OPTIONS = [
|
||||||
|
{ label: "危险化学品", value: "DANGEROUS_CHEMICAL" },
|
||||||
|
{ label: "非煤矿山", value: "NON_COAL_MINE" },
|
||||||
|
{ label: "城镇燃气", value: "URBAN_GAS" },
|
||||||
|
{ label: "一般工贸", value: "GENERAL_TRADE" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const BusinessReminder = (props) => {
|
||||||
|
const [searchForm] = Form.useForm();
|
||||||
|
const [dataSource, setDataSource] = useState([]);
|
||||||
|
const [total, setTotal] = useState(0);
|
||||||
|
const [statData, setStatData] = useState({});
|
||||||
|
|
||||||
|
const { projectWaringRemindPageLoading } = props.safetyEvalBusiness;
|
||||||
|
|
||||||
|
const getStat = async () => {
|
||||||
|
const res = await props.projectWaringStat();
|
||||||
|
if (res?.success !== false) {
|
||||||
|
setStatData(res?.data || {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
const params = {
|
||||||
|
...router.query,
|
||||||
|
current: router.query.current || 1,
|
||||||
|
size: router.query.size || 10,
|
||||||
|
};
|
||||||
|
const res = await props.projectWaringRemindPage(params);
|
||||||
|
if (res?.success !== false) {
|
||||||
|
setDataSource(res?.data || []);
|
||||||
|
setTotal(res?.totalCount || 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
searchForm.setFieldsValue(router.query);
|
||||||
|
getStat();
|
||||||
|
getData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSearch = (values) => {
|
||||||
|
router.query = { ...router.query, ...values, current: 1, size: 10 };
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = (values) => {
|
||||||
|
searchForm.resetFields();
|
||||||
|
router.query = { ...values, current: 1, size: 10 };
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (pagination) => {
|
||||||
|
router.query = {
|
||||||
|
...router.query,
|
||||||
|
current: pagination.current,
|
||||||
|
size: pagination.pageSize,
|
||||||
|
};
|
||||||
|
getData();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleViewCustomer = (record) => {
|
||||||
|
// TODO: 查看企业资料
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatCycle = (days) => {
|
||||||
|
if (!days) return "-";
|
||||||
|
const years = days / 365;
|
||||||
|
if (years >= 1) {
|
||||||
|
return Number.isInteger(years) ? `${years}年` : `${years.toFixed(1)}年`;
|
||||||
|
}
|
||||||
|
return `${days}天`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderWaringStatus = (status) => {
|
||||||
|
const cfg = WARING_STATUS_MAP[status];
|
||||||
|
if (!cfg) return <Tag>{status}</Tag>;
|
||||||
|
const labelMap = {
|
||||||
|
first: "首次预警 · 6个月",
|
||||||
|
second: "二次提醒 · 3个月",
|
||||||
|
urgent: "紧急预警 · 1个月",
|
||||||
|
overdue: "已逾期",
|
||||||
|
normal: "正常",
|
||||||
|
};
|
||||||
|
return <Tag color={cfg.color}>{labelMap[status] || cfg.text}</Tag>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ title: "客户名称", dataIndex: "customerName", width: 180, ellipsis: true },
|
||||||
|
{ title: "行业 / 场景", dataIndex: "industryCode", width: 160, ellipsis: true },
|
||||||
|
{ title: "最近报告完成日", dataIndex: "archiveDate", width: 140 },
|
||||||
|
{
|
||||||
|
title: "周期",
|
||||||
|
dataIndex: "cycleDate",
|
||||||
|
width: 100,
|
||||||
|
render: (val) => formatCycle(val),
|
||||||
|
},
|
||||||
|
{ title: "下次应开展", dataIndex: "reassessmentDate", width: 120 },
|
||||||
|
{
|
||||||
|
title: "提醒等级",
|
||||||
|
dataIndex: "waringStatus",
|
||||||
|
width: 140,
|
||||||
|
render: (val) => renderWaringStatus(val),
|
||||||
|
},
|
||||||
|
{ title: "业务负责人", dataIndex: "projectLeaderName", width: 100, ellipsis: true },
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
width: 120,
|
||||||
|
fixed: "right",
|
||||||
|
render: (_, record) => (
|
||||||
|
<Button type="link" size="small" onClick={() => handleViewCustomer(record)}>
|
||||||
|
查看企业资料
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageLayout title="安评业务提醒">
|
||||||
|
|
||||||
|
<Row gutter={16} className="br-stat-row">
|
||||||
|
<Col span={6}>
|
||||||
|
<Card size="small">
|
||||||
|
<Statistic
|
||||||
|
title="纳入提醒客户"
|
||||||
|
value={statData.remindCount ?? "-"}
|
||||||
|
suffix={<span className="br-stat-suffix">按行业周期和属地要求维护</span>}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<Card size="small">
|
||||||
|
<Statistic
|
||||||
|
title="首次预警"
|
||||||
|
value={statData.firstWaringCount ?? "-"}
|
||||||
|
valueStyle={{ color: "#52c41a" }}
|
||||||
|
suffix={<span className="br-stat-suffix">到期前6个月启动商务对接</span>}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<Card size="small">
|
||||||
|
<Statistic
|
||||||
|
title="二次提醒"
|
||||||
|
value={statData.secondWaringCount ?? "-"}
|
||||||
|
valueStyle={{ color: "#faad14" }}
|
||||||
|
suffix={<span className="br-stat-suffix">到期前3个月正式委托启动</span>}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<Card size="small">
|
||||||
|
<Statistic
|
||||||
|
title="紧急预警"
|
||||||
|
value={statData.emergencyWaringCount ?? "-"}
|
||||||
|
valueStyle={{ color: "#ff4d4f" }}
|
||||||
|
suffix={<span className="br-stat-suffix">到期前1个月加快编制评审</span>}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<div className="br-note">
|
||||||
|
<strong>提醒时间规则:</strong>
|
||||||
|
到期前6个月首次预警,提示启动商务对接和项目排期;到期前3个月二次提醒,提示正式委托启动;到期前1个月紧急预警,提示加快报告编制与评审;超过到期日自动转为已逾期。
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SearchForm
|
||||||
|
form={searchForm}
|
||||||
|
loading={false}
|
||||||
|
style={{ marginBottom: 16 }}
|
||||||
|
formLine={[
|
||||||
|
<Form.Item key="customerName" name="customerName">
|
||||||
|
<ControlWrapper.Input
|
||||||
|
label="客户名称"
|
||||||
|
placeholder="输入客户名称"
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="industryCode" name="industryCode">
|
||||||
|
<ControlWrapper.Select
|
||||||
|
label="行业 / 场景"
|
||||||
|
placeholder="全部"
|
||||||
|
allowClear
|
||||||
|
options={INDUSTRY_OPTIONS}
|
||||||
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="waringStatus" name="waringStatus">
|
||||||
|
<ControlWrapper.Select
|
||||||
|
label="提醒等级"
|
||||||
|
placeholder="全部"
|
||||||
|
allowClear
|
||||||
|
options={WARING_STATUS_OPTIONS}
|
||||||
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
]}
|
||||||
|
onFinish={handleSearch}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="br-toolbar">
|
||||||
|
<span className="br-toolbar-hint">
|
||||||
|
下一次应开展日期 = 最近一次正式报告完成日期 + 对应行业评价周期
|
||||||
|
</span>
|
||||||
|
<Space>
|
||||||
|
<Button>导出清单</Button>
|
||||||
|
<Button type="primary">批量提醒</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
rowKey="id"
|
||||||
|
columns={columns}
|
||||||
|
dataSource={dataSource}
|
||||||
|
loading={projectWaringRemindPageLoading}
|
||||||
|
scroll={{ y: props.scrollY, x: 1200 }}
|
||||||
|
pagination={{
|
||||||
|
total,
|
||||||
|
current: router.query.current || 1,
|
||||||
|
pageSize: router.query.size || 10,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (total) => `共 ${total} 条`,
|
||||||
|
}}
|
||||||
|
onChange={handlePageChange}
|
||||||
|
/>
|
||||||
|
</PageLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Connect(
|
||||||
|
[NS_SAFETY_EVAL_BUSINESS],
|
||||||
|
true,
|
||||||
|
)(AntdTableFuncControl(BusinessReminder));
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
.br {
|
||||||
|
&-header-desc {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #999;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-stat-row {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-stat-suffix {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-note {
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: #f6f8fa;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-toolbar-hint {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue