2026-07-24 11:05:09 +08:00
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
|
|
|
|
import {
|
|
|
|
|
|
Tag,
|
|
|
|
|
|
Button,
|
|
|
|
|
|
Card,
|
|
|
|
|
|
Table,
|
|
|
|
|
|
Select,
|
|
|
|
|
|
Input,
|
|
|
|
|
|
Flex,
|
|
|
|
|
|
Typography,
|
|
|
|
|
|
Row,
|
|
|
|
|
|
Col,
|
|
|
|
|
|
message,
|
2026-07-24 15:08:29 +08:00
|
|
|
|
Tooltip,
|
2026-07-28 18:12:06 +08:00
|
|
|
|
Collapse,
|
2026-07-24 11:05:09 +08:00
|
|
|
|
} from "antd";
|
|
|
|
|
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
|
|
|
|
|
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
|
|
|
|
|
|
import { tools } from "@cqsjjb/jjb-common-lib";
|
2026-07-20 15:40:12 +08:00
|
|
|
|
|
|
|
|
|
|
const { TextArea } = Input;
|
2026-07-24 11:05:09 +08:00
|
|
|
|
const { router } = tools;
|
2026-07-20 15:40:12 +08:00
|
|
|
|
|
2026-07-24 11:05:09 +08:00
|
|
|
|
const CONCLUSION_OPTIONS = [
|
|
|
|
|
|
{ value: "conform_to", label: "符合" },
|
|
|
|
|
|
{ value: "not_conform_to", label: "需修改" },
|
2026-07-20 15:40:12 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
2026-07-30 09:59:44 +08:00
|
|
|
|
const InternalReviewContent = ({ readOnly, ...props }) => {
|
2026-07-24 11:05:09 +08:00
|
|
|
|
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("");
|
2026-07-24 15:08:29 +08:00
|
|
|
|
const [selectedReviewer, setSelectedReviewer] = useState(undefined);
|
2026-07-24 11:05:09 +08:00
|
|
|
|
|
|
|
|
|
|
const fetchDetail = () => {
|
2026-07-24 15:08:29 +08:00
|
|
|
|
props.evalInternalReviewDetail({ projectId }).then((res) => {
|
|
|
|
|
|
if (res?.success && res.data.signTaskPersonCo) {
|
|
|
|
|
|
setSelectedReviewer(res.data.signTaskPersonCo?.signerPersonnelId);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-07-24 11:05:09 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (projectId) fetchDetail();
|
|
|
|
|
|
}, [projectId]);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (reviewItems) {
|
|
|
|
|
|
setRows(
|
|
|
|
|
|
reviewItems.map((item) => ({
|
|
|
|
|
|
...item,
|
2026-08-03 15:45:58 +08:00
|
|
|
|
reviewConclusion: item.reviewConclusion || "conform_to",
|
2026-07-24 11:05:09 +08:00
|
|
|
|
_key: item.id || item.reviewDimensionCode,
|
|
|
|
|
|
})),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (internalReview?.opinionContent) {
|
|
|
|
|
|
setOverallOpinion(internalReview.opinionContent);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [internalReviewDetail]);
|
2026-07-20 15:40:12 +08:00
|
|
|
|
|
|
|
|
|
|
const updateRow = (index, field, value) => {
|
|
|
|
|
|
const next = [...rows];
|
|
|
|
|
|
next[index] = { ...next[index], [field]: value };
|
|
|
|
|
|
setRows(next);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-24 11:05:09 +08:00
|
|
|
|
const isDispatched =
|
|
|
|
|
|
internalReview?.reviewerSignatureTaskStatus === "dispatched";
|
|
|
|
|
|
const problemCount = rows.filter(
|
|
|
|
|
|
(r) => r.reviewConclusion === "not_conform_to",
|
|
|
|
|
|
).length;
|
|
|
|
|
|
|
|
|
|
|
|
const handleAssign = async () => {
|
|
|
|
|
|
if (!projectId) return;
|
2026-07-28 18:12:06 +08:00
|
|
|
|
const result = await handleConfirm("not_passed");
|
|
|
|
|
|
if (!result) return;
|
2026-07-28 13:58:58 +08:00
|
|
|
|
const res = await props.evalInternalReviewAssign({
|
|
|
|
|
|
projectId,
|
|
|
|
|
|
signTaskPersonCmd: {
|
|
|
|
|
|
signerPersonnelId: selectedReviewer,
|
|
|
|
|
|
signerName: reviewers.find(
|
|
|
|
|
|
(item) => item.personnelId === selectedReviewer,
|
|
|
|
|
|
)?.personnelName,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
2026-07-24 11:05:09 +08:00
|
|
|
|
if (res?.success !== false) {
|
|
|
|
|
|
message.success("已下发签字任务");
|
2026-07-28 18:12:06 +08:00
|
|
|
|
fetchDetail();
|
2026-07-24 11:05:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleConfirm = async (resultCode) => {
|
2026-07-28 18:12:06 +08:00
|
|
|
|
if (!rows.every((r) => r.reviewConclusion)) {
|
2026-07-28 15:10:56 +08:00
|
|
|
|
message.error("请填写审核结论");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-28 13:58:58 +08:00
|
|
|
|
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?.();
|
|
|
|
|
|
}
|
2026-07-28 18:12:06 +08:00
|
|
|
|
} else {
|
2026-07-28 15:10:56 +08:00
|
|
|
|
reject();
|
2026-07-28 13:58:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-07-24 11:05:09 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const columns = [
|
|
|
|
|
|
{ title: "审核维度", dataIndex: "reviewDimensionName", width: 100 },
|
|
|
|
|
|
{ title: "审核重点", dataIndex: "reviewFocus", width: 160 },
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "结论",
|
|
|
|
|
|
dataIndex: "reviewConclusion",
|
|
|
|
|
|
width: 120,
|
|
|
|
|
|
render: (val, _, idx) => (
|
|
|
|
|
|
<Select
|
2026-08-03 15:45:58 +08:00
|
|
|
|
value={val}
|
2026-07-24 11:05:09 +08:00
|
|
|
|
size="small"
|
2026-07-28 13:58:58 +08:00
|
|
|
|
style={{ width: 130 }}
|
|
|
|
|
|
allowClear
|
2026-07-30 09:59:44 +08:00
|
|
|
|
disabled={readOnly}
|
2026-07-28 13:58:58 +08:00
|
|
|
|
placeholder="请选择结论"
|
2026-07-24 11:05:09 +08:00
|
|
|
|
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
|
2026-07-30 09:59:44 +08:00
|
|
|
|
disabled={readOnly}
|
2026-07-24 11:05:09 +08:00
|
|
|
|
placeholder="请输入审核意见"
|
|
|
|
|
|
maxLength={80}
|
|
|
|
|
|
onChange={(e) => updateRow(idx, "reviewOpinion", e.target.value)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
2026-07-20 15:40:12 +08:00
|
|
|
|
return (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div className="pf-permission">
|
2026-07-24 11:05:09 +08:00
|
|
|
|
<Tag color="blue" className="pf-tag">
|
|
|
|
|
|
内部审核人
|
|
|
|
|
|
</Tag>
|
2026-07-20 15:40:12 +08:00
|
|
|
|
<span className="pf-desc">审核、修改、反馈、验证闭环</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-24 11:05:09 +08:00
|
|
|
|
<Row gutter={16} style={{ marginBottom: 16 }}>
|
|
|
|
|
|
<Col span={8}>
|
|
|
|
|
|
<Card size="small">
|
|
|
|
|
|
<Typography.Text type="secondary">内审人</Typography.Text>
|
2026-07-28 13:58:58 +08:00
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
overflow: "hidden",
|
|
|
|
|
|
textOverflow: "ellipsis",
|
|
|
|
|
|
whiteSpace: "nowrap",
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Tooltip
|
|
|
|
|
|
title={(reviewers || []).map((r) => r.personnelName).join("、")}
|
|
|
|
|
|
>
|
|
|
|
|
|
<strong>
|
|
|
|
|
|
{(reviewers || []).map((r) => r.personnelName).join("、") ||
|
|
|
|
|
|
"-"}
|
|
|
|
|
|
</strong>
|
2026-07-24 15:08:29 +08:00
|
|
|
|
</Tooltip>
|
2026-07-24 11:05:09 +08:00
|
|
|
|
</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}
|
2026-07-28 13:58:58 +08:00
|
|
|
|
allowClear
|
2026-07-30 09:59:44 +08:00
|
|
|
|
disabled={readOnly}
|
2026-07-28 13:58:58 +08:00
|
|
|
|
placeholder="请输入总体审核意见"
|
|
|
|
|
|
maxLength={120}
|
2026-07-24 11:05:09 +08:00
|
|
|
|
onChange={(e) => setOverallOpinion(e.target.value)}
|
|
|
|
|
|
style={{ marginTop: 4 }}
|
2026-07-20 15:40:12 +08:00
|
|
|
|
/>
|
2026-07-24 11:05:09 +08:00
|
|
|
|
</div>
|
2026-07-20 15:40:12 +08:00
|
|
|
|
|
2026-07-24 11:05:09 +08:00
|
|
|
|
<Flex justify="space-between" align="center" style={{ marginTop: 10 }}>
|
|
|
|
|
|
<Flex gap={8} align="center">
|
|
|
|
|
|
<Typography.Text type="secondary">内审人独立签字</Typography.Text>
|
2026-07-24 15:08:29 +08:00
|
|
|
|
<Select
|
|
|
|
|
|
placeholder="请选择内审人"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
style={{ width: 140 }}
|
|
|
|
|
|
value={selectedReviewer}
|
|
|
|
|
|
onChange={setSelectedReviewer}
|
2026-07-30 09:59:44 +08:00
|
|
|
|
disabled={readOnly}
|
2026-07-24 15:08:29 +08:00
|
|
|
|
options={(reviewers || []).map((r) => ({
|
|
|
|
|
|
value: r.personnelId,
|
|
|
|
|
|
label: r.personnelName,
|
|
|
|
|
|
}))}
|
|
|
|
|
|
/>
|
2026-07-24 11:05:09 +08:00
|
|
|
|
<Button
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
onClick={handleAssign}
|
2026-07-28 18:12:06 +08:00
|
|
|
|
loading={internalReviewAssignLoading || props.safetyEvalBusiness?.internalReviewConfirmLoading}
|
2026-07-30 09:59:44 +08:00
|
|
|
|
disabled={isDispatched || !selectedReviewer || readOnly}
|
2026-07-24 11:05:09 +08:00
|
|
|
|
>
|
|
|
|
|
|
{isDispatched ? "已下发APP签字" : "下发APP签字"}
|
|
|
|
|
|
</Button>
|
2026-07-20 15:40:12 +08:00
|
|
|
|
</Flex>
|
2026-07-24 11:05:09 +08:00
|
|
|
|
</Flex>
|
2026-07-28 18:12:06 +08:00
|
|
|
|
<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>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
]}
|
|
|
|
|
|
/>
|
2026-07-30 09:59:44 +08:00
|
|
|
|
{!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>
|
|
|
|
|
|
)}
|
2026-07-20 15:40:12 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
2026-07-20 15:03:26 +08:00
|
|
|
|
|
2026-07-24 11:05:09 +08:00
|
|
|
|
export default Connect([NS_SAFETY_EVAL_BUSINESS], true)(InternalReviewContent);
|