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

213 lines
6.4 KiB
JavaScript
Raw Normal View History

2026-07-24 11:05:09 +08:00
import React, { useState, useEffect } from "react";
import { Tag, Button, Card, Row, Col, Table, Select, Input, Flex, Typography, message } 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-24 11:05:09 +08:00
const TechnicalReviewContent = (props) => {
const projectId = router.query?.id;
const { techReviewDetail } = props.safetyEvalBusiness || {};
const { techReview, reviewers, reviewItems } = techReviewDetail || {};
const [rows, setRows] = useState([]);
const [overallOpinion, setOverallOpinion] = useState("");
const fetchDetail = () => {
props.evalTechReviewDetail({ projectId });
};
useEffect(() => {
if (projectId) fetchDetail();
}, [projectId]);
useEffect(() => {
if (reviewItems) {
setRows(
reviewItems.map((item) => ({
...item,
_key: item.id || item.reviewDimensionCode,
})),
);
}
if (techReview?.opinionContent) {
setOverallOpinion(techReview.opinionContent);
}
}, [techReviewDetail]);
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 reviewerName = techReview?.reviewerName || "";
const isDispatched = techReview?.reviewerSignatureTaskStatus !== "non_dispatched";
const staffMatch = techReview?.staffMatchCode === 1 ? "满足" : "不满足";
const inBizScope = techReview?.inBizScopeFlag === 1 ? "在范围内" : "不在范围内";
const handleAssign = async () => {
if (!projectId) return;
const res = await props.evalTechReviewAssign({ projectId });
if (res?.success !== false) {
message.success("已下发签字任务");
fetchDetail();
}
};
const handleConfirm = async (resultCode) => {
if (!projectId) return;
const payload = {
id: techReview?.id,
projectId,
resultCode,
opinionContent: overallOpinion,
reviewItems: rows.map((r) => ({
bizType: "eval_tech_review",
bizId: techReview?.id,
reviewDimensionCode: r.reviewDimensionCode,
reviewDimensionName: r.reviewDimensionName,
reviewFocus: r.reviewFocus,
reviewConclusion: r.reviewConclusion,
reviewOpinion: r.reviewOpinion,
})),
};
const res = await props.evalTechReviewConfirm(payload);
if (res?.success !== false) {
message.success(resultCode === "1" ? "已确认技审并提交过程控制" : "已保存");
fetchDetail();
}
};
const columns = [
{ title: "审核维度", dataIndex: "reviewDimensionName", width: 120 },
{ title: "技术审核重点", dataIndex: "reviewFocus", width: 200 },
{
title: "结论",
dataIndex: "reviewConclusion",
width: 120,
render: (val, _, idx) => (
<Select
value={val}
size="small"
style={{ width: 100 }}
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",
render: (val, _, idx) => (
<Input
size="small"
placeholder="请输入技术意见"
maxLength={80}
value={val}
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>
<div>
<strong>{reviewerName || "-"}</strong>
</div>
</Card>
</Col>
<Col span={8}>
<Card size="small">
<Typography.Text type="secondary">专业匹配</Typography.Text>
<div>
<Typography.Text strong type={techReview?.staffMatchCode === 1 ? "success" : "warning"}>
{staffMatch}
</Typography.Text>
</div>
</Card>
</Col>
<Col span={8}>
<Card size="small">
<Typography.Text type="secondary">资质范围</Typography.Text>
<div>
<Typography.Text strong type={techReview?.inBizScopeFlag === 1 ? "success" : "warning"}>
{inBizScope}
</Typography.Text>
</div>
</Card>
</Col>
</Row>
<Table
dataSource={rows}
columns={columns}
rowKey="_key"
pagination={false}
size="small"
/>
<div style={{ marginTop: 12 }}>
<Typography.Text strong>技术审核意见</Typography.Text>
<TextArea
rows={2}
value={overallOpinion}
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>
<strong>{reviewerName}</strong>
<Button
size="small"
onClick={handleAssign}
loading={props.safetyEvalBusiness?.techReviewAssignLoading}
disabled={isDispatched}
>
{isDispatched ? "已下发APP签字" : "下发APP签字"}
</Button>
2026-07-20 15:40:12 +08:00
</Flex>
2026-07-24 11:05:09 +08:00
</Flex>
2026-07-20 15:40:12 +08:00
2026-07-24 11:05:09 +08:00
<Flex justify="flex-end" gap={8} style={{ marginTop: 16 }}>
<Button onClick={() => handleConfirm("2")}>保存意见</Button>
<Button
type="primary"
onClick={() => handleConfirm("1")}
loading={props.safetyEvalBusiness?.techReviewConfirmLoading}
>
确认技审并提交过程控制
</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)(TechnicalReviewContent);