337 lines
10 KiB
JavaScript
337 lines
10 KiB
JavaScript
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);
|