safety-eval-service-frontend/src/pages/Container/SafetyEvalBusiness/ProjectFlow/RiskAnalysisContent.js

568 lines
20 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, { useEffect, useState, useRef } from "react";
import {
Tag,
Button,
Card,
Row,
Col,
Table,
Form,
Input,
Select,
Radio,
DatePicker,
Flex,
message,
Spin,
Modal,
} from "antd";
import dayjs from "dayjs";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
import {EVAL_TYPE_OPTIONS} from '~/enumerate/constant';
import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions";
import { tools } from "@cqsjjb/jjb-common-lib";
import AttachmentUpload from "~/components/AttachmentUpload";
const { router } = tools;
const { TextArea } = Input;
const RiskAnalysisContent = ({ readOnly, ...props }) => {
const [form] = Form.useForm();
const projectId = router.query?.id;
const {
safetyEvalBusiness,
evalRiskAnalysisGet,
evalRiskAnalysisSave,
evalRiskAnalysisPeopleSave,
evalSignTaskSave,
} = props;
const {
riskAnalysisLoading,
evalProjectDetailLoading,
riskAnalysisSaveLoading,
evalSignTaskSaveLoading,
evalProjectDetailData,
} = safetyEvalBusiness || {};
// 查询风险分析数据回显
const [participantModalOpen, setParticipantModalOpen] = useState(false);
const [selectedParticipantIds, setSelectedParticipantIds] = useState([]);
const [signingIndex, setSigningIndex] = useState(null);
const addParticipantRef = useRef(null);
const fetchData = async () => {
if (!projectId) return;
const res = await evalRiskAnalysisGet({ projectId });
if (res?.success !== false && res?.data) {
form.setFieldsValue({
...res.data,
riskFileUrl: res.data.riskFileUrl? JSON.parse(res.data.riskFileUrl) :undefined,
analysisDate: res.data.analysisDate
? dayjs(res.data.analysisDate)
: undefined,
});
} else {
form.setFieldsValue({
evalTypeCode: evalProjectDetailData.evalTypeCode,
projectName: evalProjectDetailData.projectName,
industryName: evalProjectDetailData.industryCode,
});
}
};
useEffect(() => {
if (evalProjectDetailData?.id) {
fetchData();
}
}, [projectId, evalProjectDetailData?.id]);
// 保存
const handleSave = async (statusCode) => {
const values = await form.validateFields();
const { analysisDate, ...rest } = values;
// 检查是否有文件正在上传中
const fileList = rest.riskFileUrl || [];
if (fileList.some((f) => f.status === "uploading")) {
message.warning("文件上传中,请稍后再保存");
return;
}
if (statusCode === 2&&!rest.participants?.length) {
message.warning("请选择风险分析人员");
return;
}
const res = await evalRiskAnalysisSave({
...rest,
projectId,
statusCode,
riskFileUrl: rest.riskFileUrl? JSON.stringify(rest.riskFileUrl) :undefined,
analysisDate: analysisDate
? analysisDate.format("YYYY-MM-DD")
: undefined,
});
if (res?.success) {
let peopleSaved = true;
if (res.data && rest.participants?.length) {
const result = await evalRiskAnalysisPeopleSave(
rest.participants.map((item) => {
return {
...item,
riskId: res.data.id,
};
}),
);
peopleSaved = result?.success;
}
if (peopleSaved) {
message.success(statusCode === 2 ? "风险分析已完成" : "已暂存");
props.getDetail();
}
}
};
// 发起APP签字
const handleSignApp = async (fieldName) => {
const participant = form.getFieldValue(["participants", fieldName]);
if (!participant?.personnelId) {
message.warning("该人员信息不完整,请先保存后再发起签字");
return;
}
setSigningIndex(fieldName);
try {
const res = await evalSignTaskSave({
projectId,
nodeCode: "RISK_ANALYSIS",
bizRefId: form.getFieldValue("id") || undefined,
signerPersonnelId: participant.personnelId,
taskTitle: "风险分析签字",
taskStatusCode: 1,
});
if (res?.success) {
message.success("已发起APP签字");
form.setFieldValue(["participants", fieldName, "signStatusCode"], 1);
}
} finally {
setSigningIndex(null);
}
};
return (
<Spin spinning={evalProjectDetailLoading || riskAnalysisLoading}>
<div>
<div className="pf-permission">
<Tag color="blue" className="pf-tag">
市场部经办人 / 市场部经理
</Tag>
<span className="pf-desc">
市场部组织前置条件调查和风险分析相关职能部门参与市场部经理依据分析结果决定是否签订技术服务合同
</span>
</div>
<Card
title="安全评价风险分析记录表"
styles={{ header: { textAlign: "center" } }}
>
<Form form={form} layout="vertical" disabled={readOnly}>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="风险分析时间" name="analysisDate">
<DatePicker className="pf-w-full" allowClear />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="评价项目名称" name="projectName">
<Input
placeholder="请输入评价项目名称"
maxLength={50}
allowClear
/>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="评价类别" name="evalTypeCode">
<Select placeholder="请选择评价类别" allowClear>
{EVAL_TYPE_OPTIONS.map((opt) => (
<Select.Option key={opt.value} value={opt.value}>
{opt.label}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="所属行业" name="industryName">
<Select placeholder="请选择所属行业" allowClear>
{QUALIFICATION_INDUSTRY_OPTIONS.map((opt) => (
<Select.Option key={opt.value} value={opt.value}>
{opt.label}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
</Row>
<Form.Item label="评价范围" name="evalScope">
<TextArea
rows={2}
placeholder="明确本项目安全评价对象、装置、区域及委托范围"
maxLength={200}
showCount
/>
</Form.Item>
<Form.Item label="是否在公司资质业务范围内" name="inBizScopeFlag">
<Radio.Group>
<Radio value={1}></Radio>
<Radio value={2}></Radio>
</Radio.Group>
</Form.Item>
<Card
type="inner"
size="small"
title="一、被评价单位基本概况"
className="pf-section"
>
<Form.Item name="unitOverview">
<TextArea
rows={4}
placeholder="填写项目简介、投资规模、地理位置、周边环境、主要设施及客户要求"
maxLength={500}
showCount
/>
</Form.Item>
</Card>
<Card
type="inner"
size="small"
title="二、项目行业风险特性分析"
className="pf-section"
>
<Form.Item name="industryRiskDesc">
<TextArea
rows={3}
placeholder="分析项目所属行业的主要危险、有害因素"
maxLength={500}
showCount
/>
</Form.Item>
<div className="pf-hint-blue">
应结合项目行业特点地理位置周边环境和评价类别填写非常规项目应通过风险分析会议形成记录
</div>
</Card>
<Card
type="inner"
size="small"
title="三、评价机构符合情况"
className="pf-section"
>
<Form.Item
label="1评价人员专业构成是否满足该项目的评价需求"
name="staffMatchCode"
>
<Radio.Group>
<Radio value={1}>满足</Radio>
<Radio value={2}>不满足</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="2是否需要聘请相关技术专家"
name="needExpertCode"
>
<Radio.Group>
<Radio value={2}>不需要</Radio>
<Radio value={1}>需要</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="3是否有能力承担该项目的评价风险"
name="capabilityCode"
>
<Radio.Group>
<Radio value={1}></Radio>
<Radio value={2}></Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="4项目的可行性分析" name="feasibleCode">
<Radio.Group>
<Radio value={1}>可行</Radio>
<Radio value={2}>不可行</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="5项目的经济性分析" name="economicCode">
<Radio.Group>
<Radio value={1}>有经济效益</Radio>
<Radio value={2}>无经济效益</Radio>
</Radio.Group>
</Form.Item>
</Card>
<Card
type="inner"
size="small"
title="风险分析结论"
className="pf-section"
>
<Form.Item label="风险程度" name="riskLevelCode">
<Radio.Group>
<Radio value={1}>低度</Radio>
<Radio value={2}>中度</Radio>
<Radio value={3}>高度</Radio>
</Radio.Group>
</Form.Item>
<div className="pf-risk-note">
中度化工金属非金属矿山金属冶炼陆地油气开采等行业评价项目
</div>
<Form.Item label="合同签订" name="signContractCode">
<Radio.Group>
<Radio value={1}>签合同</Radio>
<Radio value={2}>不签合同</Radio>
</Radio.Group>
</Form.Item>
<div className="pf-hint-blue">
系统建议当前条件具备签约基础最终结论由市场部经理批准
</div>
</Card>
<Card type="inner" size="small" title="参与风险分析人员签字" className="pf-section">
<Form.List name="participants">
{(fields, { add, remove }) => {
addParticipantRef.current = add;
return (
<>
<Table
dataSource={fields}
columns={[
{
title: "姓名",
dataIndex: "name",
render: (_, field) => {
const { key, ...fieldProps } = field;
return (
<Form.Item
{...fieldProps}
name={[field.name, "personnelName"]}
noStyle
>
<Input
placeholder="姓名"
disabled
maxLength={20}
allowClear
/>
</Form.Item>
);
},
},
{
title: "部门/岗位",
dataIndex: "deptName",
render: (_, field) => {
const { key, ...fieldProps } = field;
return (
<Form.Item
{...fieldProps}
name={[field.name, "deptName"]}
noStyle
>
<Input
placeholder="部门/岗位"
disabled
maxLength={20}
allowClear
/>
</Form.Item>
);
},
},
{
title: "分析意见",
dataIndex: "opinion",
render: (_, field) => {
const { key, ...fieldProps } = field;
return (
<Form.Item
{...fieldProps}
name={[field.name, "opinion"]}
noStyle
>
<Input
placeholder="填写分析意见"
maxLength={30}
allowClear
/>
</Form.Item>
);
},
},
{
title: "签字状态",
render: (_, field) => {
const value = form.getFieldValue([
"participants",
field.name,
"signStatusCode",
]);
let text = "发起APP签字";
if (value === 1) {
text = "待APP签字";
}
if (value === 2) {
text = "已手写签字";
}
return (
<Button
size="small"
loading={
evalSignTaskSaveLoading &&
signingIndex === field.name
}
disabled={value === 1 || value === 2 || readOnly}
onClick={() => handleSignApp(field.name)}
>
{text}
</Button>
);
},
},
{
title: "操作",
render: (_, field) => (
<Button
type="link"
danger
size="small"
disabled={readOnly}
onClick={() => remove(field.name)}
>
删除
</Button>
),
},
]}
rowKey={(_, index) => index}
pagination={false}
/>
{!readOnly && (
<Button
type="dashed"
size="small"
style={{ marginTop: 8 }}
onClick={() => {
setSelectedParticipantIds([]);
setParticipantModalOpen(true);
}}
>
添加参与人员
</Button>
)}
</>
);
}}
</Form.List>
</Card>
<AttachmentUpload
name="riskFileUrl"
rules={[{ required: true, message: "请上传风险分析表附件" }]}
style={{ marginBottom: 0 }}
label="风险分析表附件"
accept=".pdf,.docx,.jpg,.png"
maxCount={10}
disabled={readOnly}
extra="支持 PDF、Word、JPG、PNG 格式。上传后可替换修改,系统保留当前归档文件信息"
/>
</Form>
</Card>
<Modal
title="选择参与人员"
open={participantModalOpen}
onCancel={() => setParticipantModalOpen(false)}
onOk={() => {
const selected = (evalProjectDetailData?.participants || []).filter(
(p) => selectedParticipantIds.includes(p.id),
);
selected.forEach((p) => {
addParticipantRef.current?.({
personnelId: p.id,
personnelName: p.name,
deptName: p.deptName,
opinion: "",
projectId,
});
});
setParticipantModalOpen(false);
}}
>
<Table
dataSource={(() => {
const existing = (form.getFieldValue("participants") || []).map(
(p) => p.personnelName,
);
return (evalProjectDetailData?.participants || []).filter(
(p) => !existing.includes(p.name),
);
})()}
rowKey="id"
rowSelection={{
type: "checkbox",
selectedRowKeys: selectedParticipantIds,
onChange: (keys) => setSelectedParticipantIds(keys),
getCheckboxProps: (record) => ({
disabled: false,
}),
}}
columns={[
{ title: "姓名", dataIndex: "name", width: 100 },
{ title: "部门", dataIndex: "deptName", width: 120 },
{ title: "岗位", dataIndex: "posName", width: 120 },
{ title: "能力", dataIndex: "capacity", width: 120 },
]}
pagination={false}
size="small"
locale={{ emptyText: "所有人员均已添加" }}
/>
</Modal>
{!readOnly && (
<Flex justify="flex-end" gap={8} style={{marginTop: '8px'}}>
<Button
onClick={() => handleSave(1)}
loading={riskAnalysisSaveLoading}
>
暂存
</Button>
<Button
type="primary"
onClick={() => handleSave(2)}
loading={riskAnalysisSaveLoading}
>
完成分析并保存
</Button>
</Flex>
)}
</div>
</Spin>
);
};
export default Connect([NS_SAFETY_EVAL_BUSINESS], true)(RiskAnalysisContent);