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

337 lines
10 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 {
Tag,
Button,
Card,
Table,
Select,
Input,
Flex,
Typography,
Row,
Col,
message,
Tooltip,
Collapse,
} from "antd";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
import { tools } from "@cqsjjb/jjb-common-lib";
const { TextArea } = Input;
const { router } = tools;
const CONCLUSION_OPTIONS = [
{ value: "conform_to", label: "符合" },
{ value: "not_conform_to", label: "需修改" },
];
const InternalReviewContent = ({ readOnly, ...props }) => {
const projectId = router.query?.id;
const {
internalReviewDetail,
internalReviewAssignLoading,
internalReviewConfirmLoading,
internalReviewDetailLoading,
} = props.safetyEvalBusiness || {};
const { internalReview, reviewers, reviewItems } = internalReviewDetail || {};
const [rows, setRows] = useState([]);
const [overallOpinion, setOverallOpinion] = useState("");
const [selectedReviewer, setSelectedReviewer] = useState(undefined);
const fetchDetail = () => {
props.evalInternalReviewDetail({ projectId }).then((res) => {
if (res?.success && res.data.signTaskPersonCo) {
setSelectedReviewer(res.data.signTaskPersonCo?.signerPersonnelId);
}
});
};
useEffect(() => {
if (projectId) fetchDetail();
}, [projectId]);
useEffect(() => {
if (reviewItems) {
setRows(
reviewItems.map((item) => ({
...item,
reviewConclusion: item.reviewConclusion || "conform_to",
_key: item.id || item.reviewDimensionCode,
})),
);
}
if (internalReview?.opinionContent) {
setOverallOpinion(internalReview.opinionContent);
}
}, [internalReviewDetail]);
const updateRow = (index, field, value) => {
const next = [...rows];
next[index] = { ...next[index], [field]: value };
setRows(next);
};
const isDispatched =
internalReview?.reviewerSignatureTaskStatus === "dispatched";
const problemCount = rows.filter(
(r) => r.reviewConclusion === "not_conform_to",
).length;
const handleAssign = async () => {
if (!projectId) return;
const result = await handleConfirm("not_passed");
if (!result) return;
const res = await props.evalInternalReviewAssign({
projectId,
signTaskPersonCmd: {
signerPersonnelId: selectedReviewer,
signerName: reviewers.find(
(item) => item.personnelId === selectedReviewer,
)?.personnelName,
},
});
if (res?.success !== false) {
message.success("已下发签字任务");
fetchDetail();
}
};
const handleConfirm = async (resultCode) => {
if (!rows.every((r) => r.reviewConclusion)) {
message.error("请填写审核结论");
return;
}
return new Promise(async (resolve, reject) => {
if (!projectId) return;
const payload = {
id: internalReview?.id,
projectId,
resultCode,
opinionContent: overallOpinion,
reviewItems: rows.map((r) => ({
bizType: "eval_internal_review",
bizId: internalReview?.id,
reviewDimensionCode: r.reviewDimensionCode,
reviewDimensionName: r.reviewDimensionName,
reviewFocus: r.reviewFocus,
reviewConclusion: r.reviewConclusion,
reviewOpinion: r.reviewOpinion,
})),
};
const res = await props.evalInternalReviewConfirm(payload);
if (res?.success !== false) {
message.success(resultCode === "passed" ? "已提交" : "已保存");
resolve(res);
if (resultCode === "passed") {
props.getDetail?.();
}
} else {
reject();
}
});
};
const columns = [
{ title: "审核维度", dataIndex: "reviewDimensionName", width: 100 },
{ title: "审核重点", dataIndex: "reviewFocus", width: 160 },
{
title: "结论",
dataIndex: "reviewConclusion",
width: 120,
render: (val, _, idx) => (
<Select
value={val}
size="small"
style={{ width: 130 }}
allowClear
disabled={readOnly}
placeholder="请选择结论"
onChange={(v) => updateRow(idx, "reviewConclusion", v)}
>
{CONCLUSION_OPTIONS.map((opt) => (
<Select.Option key={opt.value} value={opt.value}>
{opt.label}
</Select.Option>
))}
</Select>
),
},
{
title: "审核意见",
dataIndex: "reviewOpinion",
width: 180,
render: (val, _, idx) => (
<Input
size="small"
value={val}
allowClear
disabled={readOnly}
placeholder="请输入审核意见"
maxLength={80}
onChange={(e) => updateRow(idx, "reviewOpinion", e.target.value)}
/>
),
},
];
return (
<div>
<div className="pf-permission">
<Tag color="blue" className="pf-tag">
内部审核人
</Tag>
<span className="pf-desc">审核修改反馈验证闭环</span>
</div>
<Row gutter={16} style={{ marginBottom: 16 }}>
<Col span={8}>
<Card size="small">
<Typography.Text type="secondary">内审人</Typography.Text>
<div
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
<Tooltip
title={(reviewers || []).map((r) => r.personnelName).join("、")}
>
<strong>
{(reviewers || []).map((r) => r.personnelName).join("、") ||
"-"}
</strong>
</Tooltip>
</div>
</Card>
</Col>
<Col span={8}>
<Card size="small">
<Typography.Text type="secondary">审核项</Typography.Text>
<div>
<strong>{rows.length}</strong>
</div>
</Card>
</Col>
<Col span={8}>
<Card size="small">
<Typography.Text type="secondary">问题</Typography.Text>
<div>
<Typography.Text
strong
type={problemCount > 0 ? "warning" : undefined}
>
{problemCount}
</Typography.Text>
</div>
</Card>
</Col>
</Row>
<Table
dataSource={rows}
columns={columns}
rowKey="_key"
pagination={false}
loading={internalReviewDetailLoading}
size="small"
/>
<div style={{ marginTop: 12 }}>
<Typography.Text strong>总体审核意见</Typography.Text>
<TextArea
rows={2}
value={overallOpinion}
allowClear
disabled={readOnly}
placeholder="请输入总体审核意见"
maxLength={120}
onChange={(e) => setOverallOpinion(e.target.value)}
style={{ marginTop: 4 }}
/>
</div>
<Flex justify="space-between" align="center" style={{ marginTop: 10 }}>
<Flex gap={8} align="center">
<Typography.Text type="secondary">内审人独立签字</Typography.Text>
<Select
placeholder="请选择内审人"
size="small"
style={{ width: 140 }}
value={selectedReviewer}
onChange={setSelectedReviewer}
disabled={readOnly}
options={(reviewers || []).map((r) => ({
value: r.personnelId,
label: r.personnelName,
}))}
/>
<Button
size="small"
onClick={handleAssign}
loading={internalReviewAssignLoading || props.safetyEvalBusiness?.internalReviewConfirmLoading}
disabled={isDispatched || !selectedReviewer || readOnly}
>
{isDispatched ? "已下发APP签字" : "下发APP签字"}
</Button>
</Flex>
</Flex>
<Collapse
style={{marginTop: '12px'}}
items={[
{
key: "checklist",
label: "内部审核完整核查清单13项",
children: (
<ol
style={{
margin: 0,
paddingLeft: 20,
fontSize: 13,
color: "#475569",
lineHeight: 1.8,
}}
>
<li>评价依据是否充分有效</li>
<li>危险有害因素识别是否全面</li>
<li>评价单元划分是否合理</li>
<li>评价方法选择是否适当</li>
<li>对策措施与建议是否可行</li>
<li>报告编制格式是否符合要求</li>
<li>报告文字数据及其他内容是否准确</li>
<li>评价范围是否与项目任务通知书一致</li>
<li>是否保留现场影像记录</li>
<li>评价结论是否正确</li>
<li>评价对象情况描述是否存在重大遗漏或严重失实</li>
<li>生产工艺设备参数危险源控制点是否阐述清楚</li>
<li>重大危险源重大隐患辨识是否准确</li>
</ol>
),
},
]}
/>
{!readOnly && (
<Flex justify="flex-end" gap={8} style={{ marginTop: 16 }}>
<Button
loading={props.safetyEvalBusiness?.internalReviewConfirmLoading}
onClick={() => handleConfirm("not_passed")}
>
保存意见
</Button>
<Button
type="primary"
onClick={() => handleConfirm("passed")}
loading={props.safetyEvalBusiness?.internalReviewConfirmLoading}
>
确认修改并提交技审
</Button>
</Flex>
)}
</div>
);
};
export default Connect([NS_SAFETY_EVAL_BUSINESS], true)(InternalReviewContent);