381 lines
12 KiB
JavaScript
381 lines
12 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
||
import {
|
||
Button,
|
||
Card,
|
||
Table,
|
||
Select,
|
||
Input,
|
||
Flex,
|
||
Typography,
|
||
message,
|
||
Collapse,
|
||
} from "antd";
|
||
import { CheckCircleFilled } from "@ant-design/icons";
|
||
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 ControlContent = ({ readOnly, ...props }) => {
|
||
const projectId = router.query?.id;
|
||
const { processControlDetail } = props.safetyEvalBusiness || {};
|
||
const { processControl, controllers, reviewItems, orgHeads } =
|
||
processControlDetail || {};
|
||
|
||
const [selectPeople, setSelectPeople] = useState(null);
|
||
|
||
// 当前步骤:1-过程控制审核 2-机构负责人签发
|
||
const [step, setStep] = useState(1);
|
||
|
||
// 过程控制审核
|
||
const [rows, setRows] = useState([]);
|
||
|
||
// 机构负责人签发
|
||
const [approvalOpinion, setApprovalOpinion] = useState("");
|
||
|
||
const fetchDetail = () => {
|
||
props.evalProcessControlDetail({ projectId }).then((res) => {
|
||
if (res?.success) {
|
||
if (res?.data?.signTaskPersonCo) {
|
||
setSelectPeople({
|
||
value: res?.data?.signTaskPersonCo?.signerPersonnelId,
|
||
label: res?.data?.signTaskPersonCo?.signerName,
|
||
});
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (projectId) {
|
||
fetchDetail();
|
||
}
|
||
}, [projectId]);
|
||
|
||
useEffect(() => {
|
||
if (reviewItems) {
|
||
setRows(
|
||
reviewItems.map((item) => ({
|
||
...item,
|
||
reviewConclusion: item.reviewConclusion || "conform_to",
|
||
_key: item.id || item.reviewDimensionCode,
|
||
})),
|
||
);
|
||
}
|
||
|
||
if (processControl?.opinionContent) {
|
||
setApprovalOpinion(processControl.opinionContent);
|
||
}
|
||
}, [processControlDetail]);
|
||
|
||
const updateRow = (index, field, value) => {
|
||
const next = [...rows];
|
||
next[index] = { ...next[index], [field]: value };
|
||
setRows(next);
|
||
};
|
||
|
||
const isDispatched = processControl?.reviewerSignatureTaskStatus !== "completed";
|
||
const isOrgHeadSignature = processControl?.orgHeadSignatureTaskStatus !== "non_dispatched";
|
||
|
||
// 过程控制签字提交(confirm)
|
||
const handleConfirm = async (resultCode) => {
|
||
if (!projectId) return;
|
||
if (!rows.every((r) => r.reviewConclusion)) {
|
||
message.error("请填写所有核查项的结果");
|
||
return;
|
||
}
|
||
const payload = {
|
||
id: processControl?.id,
|
||
projectId,
|
||
resultCode,
|
||
opinionContent: approvalOpinion,
|
||
signTaskPersonCmd: {
|
||
signerPersonnelId: controllers?.[0]?.personnelId,
|
||
signerName: controllers?.[0]?.personnelName,
|
||
},
|
||
reviewItems: rows.map((r) => ({
|
||
bizType: "eval_process_control",
|
||
bizId: processControl?.id,
|
||
reviewDimensionCode: r.reviewDimensionCode,
|
||
reviewDimensionName: r.reviewDimensionName,
|
||
reviewFocus: r.reviewFocus,
|
||
reviewConclusion: r.reviewConclusion,
|
||
reviewOpinion: r.reviewOpinion,
|
||
})),
|
||
};
|
||
const res = await props.evalProcessControlConfirm(payload);
|
||
if (res?.success !== false) {
|
||
message.success(
|
||
resultCode === "passed" ? "已提交通过审核" : "已提交退回修改",
|
||
);
|
||
fetchDetail();
|
||
}
|
||
};
|
||
|
||
// 下发签字任务(assign)
|
||
const handleAssign = async () => {
|
||
if (!projectId) return;
|
||
const res = await props.evalProcessControlAssign({
|
||
projectId,
|
||
signTaskPersonCmd: {
|
||
signerPersonnelId: selectPeople?.value,
|
||
signerName: selectPeople?.label,
|
||
},
|
||
opinionContent: approvalOpinion,
|
||
});
|
||
if (res?.success !== false) {
|
||
message.success("已下发签字任务");
|
||
fetchDetail();
|
||
}
|
||
};
|
||
|
||
const reviewColumns = [
|
||
{ title: "程序审核项", dataIndex: "reviewDimensionName", width: 120 },
|
||
{ title: "核查内容", dataIndex: "reviewFocus", width: 200 },
|
||
{
|
||
title: "结果",
|
||
dataIndex: "reviewConclusion",
|
||
width: 150,
|
||
render: (val, _, idx) => (
|
||
<Select
|
||
value={val}
|
||
size="small"
|
||
style={{ width: 130 }}
|
||
allowClear
|
||
disabled={readOnly}
|
||
placeholder="请选择结果"
|
||
onChange={(v) => updateRow(idx, "reviewConclusion", v)}
|
||
>
|
||
<Select.Option value="conform_to">符合</Select.Option>
|
||
<Select.Option value="not_conform_to">不符合</Select.Option>
|
||
</Select>
|
||
),
|
||
},
|
||
{
|
||
title: "意见",
|
||
dataIndex: "reviewOpinion",
|
||
render: (val, _, idx) => (
|
||
<Input
|
||
size="small"
|
||
value={val}
|
||
allowClear
|
||
disabled={readOnly}
|
||
maxLength={60}
|
||
placeholder="请输入意见"
|
||
onChange={(e) => updateRow(idx, "reviewOpinion", e.target.value)}
|
||
/>
|
||
),
|
||
},
|
||
];
|
||
|
||
const STEP_ITEMS = [
|
||
{ key: 1, label: "过程控制审核" },
|
||
{ key: 2, label: "机构负责人签发" },
|
||
];
|
||
|
||
const reviewerSignatureTaskStatusMap={
|
||
dispatched: "已下发",
|
||
non_dispatched: "过程控制签字提交",
|
||
completed: "已完成",
|
||
}
|
||
|
||
const OrgHeadSignatureTaskStatusMap={
|
||
dispatched: "已下发",
|
||
non_dispatched: "下发APP签字",
|
||
completed: "已完成",
|
||
}
|
||
|
||
return (
|
||
<div>
|
||
<Flex
|
||
gap={0}
|
||
style={{ marginBottom: 16, borderBottom: "1px solid #e8e8e8" }}
|
||
>
|
||
{STEP_ITEMS.map((item) => (
|
||
<Button
|
||
key={item.key}
|
||
type="text"
|
||
onClick={() => setStep(item.key)}
|
||
style={{
|
||
minWidth: 128,
|
||
borderBottom:
|
||
step === item.key
|
||
? "3px solid #1677ff"
|
||
: "3px solid transparent",
|
||
borderRadius: 0,
|
||
fontWeight: step === item.key ? 700 : 400,
|
||
color: step === item.key ? "#1677ff" : undefined,
|
||
background: step === item.key ? "#e6f4ff" : "transparent",
|
||
}}
|
||
>
|
||
{item.label}
|
||
</Button>
|
||
))}
|
||
</Flex>
|
||
|
||
{step === 1 && (
|
||
<Card
|
||
title="过程控制审核"
|
||
style={{ marginBottom: 16 }}
|
||
styles={{ body: { paddingBottom: 0 } }}
|
||
>
|
||
<Table
|
||
dataSource={rows}
|
||
columns={reviewColumns}
|
||
rowKey="_key"
|
||
pagination={false}
|
||
size="small"
|
||
/>
|
||
<Collapse
|
||
style={{ marginTop: 12 }}
|
||
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>
|
||
),
|
||
},
|
||
]}
|
||
/>
|
||
<Flex justify="flex-end" gap={8} style={{ padding: "12px 0" }}>
|
||
{!readOnly && (
|
||
<Button
|
||
type="primary"
|
||
disabled={processControl?.reviewerSignatureTaskStatus !== "non_dispatched"}
|
||
onClick={() => handleConfirm("passed")}
|
||
loading={props.safetyEvalBusiness?.processControlConfirmLoading}
|
||
>
|
||
{reviewerSignatureTaskStatusMap[processControl?.reviewerSignatureTaskStatus]}
|
||
</Button>
|
||
)}
|
||
</Flex>
|
||
</Card>
|
||
)}
|
||
|
||
{step === 2 && (
|
||
<Card
|
||
title="机构负责人签发"
|
||
style={{ marginBottom: 16 }}
|
||
styles={{ body: { paddingBottom: 0 } }}
|
||
>
|
||
<div className="final-approval-card">
|
||
<div
|
||
style={{
|
||
marginBottom: 12,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 8,
|
||
}}
|
||
>
|
||
<CheckCircleFilled style={{ color: "#52c41a", fontSize: 16 }} />
|
||
<span>无可能引发法律纠纷的重大风险</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
marginBottom: 12,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 8,
|
||
}}
|
||
>
|
||
<CheckCircleFilled style={{ color: "#52c41a", fontSize: 16 }} />
|
||
<span>评价过程无违规现象</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
marginBottom: 12,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 8,
|
||
}}
|
||
>
|
||
<CheckCircleFilled style={{ color: "#52c41a", fontSize: 16 }} />
|
||
<span>全过程记录和签字完整</span>
|
||
</div>
|
||
|
||
<div style={{ marginBottom: 12 }}>
|
||
<Typography.Text strong>签发意见</Typography.Text>
|
||
<TextArea
|
||
rows={3}
|
||
value={approvalOpinion}
|
||
onChange={(e) => setApprovalOpinion(e.target.value)}
|
||
disabled={readOnly}
|
||
placeholder="请输入签发意见"
|
||
style={{ marginTop: 4 }}
|
||
/>
|
||
</div>
|
||
|
||
<Flex
|
||
justify="space-between"
|
||
align="center"
|
||
style={{ paddingBottom: 16 }}
|
||
>
|
||
<Flex gap={8} align="center">
|
||
<Typography.Text type="secondary">
|
||
评价机构负责人
|
||
</Typography.Text>
|
||
<Select
|
||
placeholder="请选择评价机构负责人"
|
||
size="small"
|
||
allowClear
|
||
style={{ width: 180 }}
|
||
disabled={isDispatched || isOrgHeadSignature || readOnly}
|
||
value={selectPeople?.value}
|
||
onChange={(value, option) => {
|
||
setSelectPeople(option);
|
||
}}
|
||
options={(orgHeads || []).map((r) => ({
|
||
value: r.personnelId,
|
||
label: r.personnelName,
|
||
}))}
|
||
/>
|
||
<Button
|
||
size="small"
|
||
onClick={handleAssign}
|
||
loading={
|
||
props.safetyEvalBusiness?.processControlAssignLoading
|
||
}
|
||
disabled={isDispatched || isOrgHeadSignature || !selectPeople?.value || readOnly}
|
||
>
|
||
{isDispatched ? "过程控制审核暂未完成" : OrgHeadSignatureTaskStatusMap[processControl?.orgHeadSignatureTaskStatus]}
|
||
</Button>
|
||
</Flex>
|
||
</Flex>
|
||
</div>
|
||
</Card>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default Connect([NS_SAFETY_EVAL_BUSINESS], true)(ControlContent);
|