Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	jjb.config.js
dev_1.2
huwei 2026-07-28 16:07:22 +08:00
commit 19bd1f998a
11 changed files with 313 additions and 176 deletions

View File

@ -4,7 +4,7 @@ import { PlusOutlined } from "@ant-design/icons";
const isImage = (url) => const isImage = (url) =>
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || ""); /\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
export default function AttachmentUpload({ name, label, disabled = false, maxCount, accept, extra ,rules }) { export default function AttachmentUpload({ name, label, disabled = false, maxCount, accept, extra ,rules, style }) {
const [previewImage, setPreviewImage] = useState(""); const [previewImage, setPreviewImage] = useState("");
return ( return (
@ -13,6 +13,7 @@ export default function AttachmentUpload({ name, label, disabled = false, maxCou
name={name} name={name}
label={label} label={label}
extra={extra} extra={extra}
style={style}
valuePropName="fileList" valuePropName="fileList"
rules={rules} rules={rules}
getValueProps={(value) => { getValueProps={(value) => {
@ -90,7 +91,7 @@ export default function AttachmentUpload({ name, label, disabled = false, maxCou
</button> </button>
</Upload> </Upload>
</Form.Item> </Form.Item>
<Image {previewImage && <Image
style={{ display: "none" }} style={{ display: "none" }}
preview={{ preview={{
visible: !!previewImage, visible: !!previewImage,
@ -99,7 +100,7 @@ export default function AttachmentUpload({ name, label, disabled = false, maxCou
if (!visible) setPreviewImage(""); if (!visible) setPreviewImage("");
}, },
}} }}
/> />}
</> </>
); );
} }

View File

@ -45,14 +45,22 @@ function saveState(tabs, activeKey) {
export default function SimulatedLayout({ children, history }) { export default function SimulatedLayout({ children, history }) {
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
const [state, setState] = useState(loadState); const [state, setState] = useState(loadState);
const currentPath = window.location.pathname; const [currentPath, setCurrentPath] = useState(window.location.pathname);
// // currentPath
useEffect(() => {
const unlisten = history?.listen?.((location) => {
setCurrentPath(location.pathname || window.location.pathname);
});
return () => unlisten?.();
}, [history]);
//
useEffect(() => { useEffect(() => {
if (currentPath && !currentPath.endsWith("/container/")) { if (currentPath && !currentPath.endsWith("/container/")) {
addTab(currentPath); addTab(currentPath);
} }
}, []); // }, [currentPath]);
/* ---- 标签操作 ---- */ /* ---- 标签操作 ---- */
const addTab = useCallback((path) => { const addTab = useCallback((path) => {
@ -73,9 +81,11 @@ export default function SimulatedLayout({ children, history }) {
const navigateTo = useCallback((path) => { const navigateTo = useCallback((path) => {
if (path !== currentPath) { if (path !== currentPath) {
window.location.href = path; history.replace(path.replace(/^\/safetyEval/, ""));
// history.listen currentPath
setCurrentPath(window.location.pathname);
} }
}, [currentPath]); }, [currentPath, history]);
const handleCloseTab = useCallback((targetKey) => { const handleCloseTab = useCallback((targetKey) => {
setState((prev) => { setState((prev) => {

View File

@ -11,6 +11,7 @@ import {
DatePicker, DatePicker,
Flex, Flex,
Tag, Tag,
Radio,
} from "antd"; } from "antd";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { tools } from "@cqsjjb/jjb-common-lib"; import { tools } from "@cqsjjb/jjb-common-lib";
@ -103,7 +104,7 @@ const EvalProjectCreate = (props) => {
name: item.userName, name: item.userName,
deptName: item.deptName, deptName: item.deptName,
posName: item.postName, posName: item.postName,
capacity: '龟派气功波', capacity: '',
})), })),
}; };
if (isView) { if (isView) {
@ -298,12 +299,13 @@ const EvalProjectCreate = (props) => {
/> />
</Form.Item> </Form.Item>
</Col> </Col>
</Row>
<Row gutter={24}>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
name="industryCode" name="industryCode"
label="所属行业" label="所属行业"
rules={[{ required: true, message: "请选择所属行业" }]} rules={[{ required: true, message: "请选择所属行业" }]}
> >
<Select placeholder="请选择" allowClear> <Select placeholder="请选择" allowClear>
{QUALIFICATION_INDUSTRY_OPTIONS.map((opt) => ( {QUALIFICATION_INDUSTRY_OPTIONS.map((opt) => (
@ -314,6 +316,18 @@ const EvalProjectCreate = (props) => {
</Select> </Select>
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}>
<Form.Item
name="is_statutory"
label="是否法定项目"
rules={[{ required: true, message: "请选择是否法定项目" }]}
>
<Radio.Group>
<Radio value={true}></Radio>
<Radio value={false}></Radio>
</Radio.Group>
</Form.Item>
</Col>
</Row> </Row>
</Card> </Card>

View File

@ -22,6 +22,7 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { tools } from "@cqsjjb/jjb-common-lib"; import { tools } from "@cqsjjb/jjb-common-lib";
import AttachmentUpload from "~/components/AttachmentUpload"; import AttachmentUpload from "~/components/AttachmentUpload";
import PreviewUrlButton from "~/components/PreviewUrlButton";
const { router } = tools; const { router } = tools;
@ -265,13 +266,7 @@ const ProjectDocLibrary = (props) => {
title: "操作", title: "操作",
width: 100, width: 100,
render: (_, record) => ( render: (_, record) => (
<Button <PreviewUrlButton url={record.project_doc_url} />
type="link"
size="small"
onClick={() => window.open(record.project_doc_url)}
>
预览
</Button>
), ),
}, },
]} ]}

View File

@ -501,10 +501,12 @@ const ContractContent = (props) => {
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item label="职工人数" name="staffCount"> <Form.Item label="职工人数" name="staffCount">
<Input <InputNumber
style={{ width: "100%" }}
placeholder="请输入职工人数" placeholder="请输入职工人数"
maxLength={10} min={0}
allowClear precision={0}
max={99999}
/> />
</Form.Item> </Form.Item>
</Col> </Col>

View File

@ -22,7 +22,7 @@ const ControlContent = (props) => {
// 过程控制审核 // 过程控制审核
const [rows, setRows] = useState([]); const [rows, setRows] = useState([]);
const [opinionContent, setOpinionContent] = useState("");
// 项目归档 // 项目归档
const [files, setFiles] = useState([]); const [files, setFiles] = useState([]);
@ -34,7 +34,7 @@ const ControlContent = (props) => {
const fetchDetail = () => { const fetchDetail = () => {
props.evalProcessControlDetail({ projectId }).then((res) => { props.evalProcessControlDetail({ projectId }).then((res) => {
if (res?.success) { if (res?.success) {
setOpinionContent(res?.data?.opinionContent);
if (res?.data?.signTaskPersonCo) { if (res?.data?.signTaskPersonCo) {
setSelectPeople({ setSelectPeople({
value: res?.data?.signTaskPersonCo?.signerPersonnelId, value: res?.data?.signTaskPersonCo?.signerPersonnelId,
@ -65,8 +65,10 @@ const ControlContent = (props) => {
})), })),
); );
} }
if (processControl?.opinionContent) { if (processControl?.opinionContent) {
setOpinionContent(processControl.opinionContent); setApprovalOpinion(processControl.opinionContent);
} }
}, [processControlDetail]); }, [processControlDetail]);
@ -93,11 +95,15 @@ const ControlContent = (props) => {
// 过程控制签字提交confirm // 过程控制签字提交confirm
const handleConfirm = async (resultCode) => { const handleConfirm = async (resultCode) => {
if (!projectId) return; if (!projectId) return;
if(!rows.every((r) => r.reviewConclusion)){
message.error("请填写所有核查项的结果");
return;
}
const payload = { const payload = {
id: processControl?.id, id: processControl?.id,
projectId, projectId,
resultCode, resultCode,
opinionContent, opinionContent: approvalOpinion,
reviewItems: rows.map((r) => ({ reviewItems: rows.map((r) => ({
bizType: "eval_process_control", bizType: "eval_process_control",
bizId: processControl?.id, bizId: processControl?.id,
@ -151,12 +157,14 @@ const ControlContent = (props) => {
{ {
title: "结果", title: "结果",
dataIndex: "reviewConclusion", dataIndex: "reviewConclusion",
width: 120, width: 150,
render: (val, _, idx) => ( render: (val, _, idx) => (
<Select <Select
value={val} value={val}
size="small" size="small"
style={{ width: 100 }} style={{ width: 130 }}
allowClear
placeholder="请选择结果"
onChange={(v) => updateRow(idx, "reviewConclusion", v)} onChange={(v) => updateRow(idx, "reviewConclusion", v)}
> >
<Select.Option value="conform_to">符合</Select.Option> <Select.Option value="conform_to">符合</Select.Option>

View File

@ -71,7 +71,6 @@ const InternalReviewContent = (props) => {
setRows(next); setRows(next);
}; };
const isDispatched = const isDispatched =
internalReview?.reviewerSignatureTaskStatus === "dispatched"; internalReview?.reviewerSignatureTaskStatus === "dispatched";
const problemCount = rows.filter( const problemCount = rows.filter(
@ -80,17 +79,29 @@ const InternalReviewContent = (props) => {
const handleAssign = async () => { const handleAssign = async () => {
if (!projectId) return; if (!projectId) return;
const res = await props.evalInternalReviewAssign({ projectId, signTaskPersonCmd:{ const result= await handleConfirm("not_passed");
if(!result) return;
const res = await props.evalInternalReviewAssign({
projectId,
signTaskPersonCmd: {
signerPersonnelId: selectedReviewer, signerPersonnelId: selectedReviewer,
signerName: reviewers.find((item) => item.personnelId === selectedReviewer)?.personnelName , signerName: reviewers.find(
} }); (item) => item.personnelId === selectedReviewer,
)?.personnelName,
},
});
if (res?.success !== false) { if (res?.success !== false) {
message.success("已下发签字任务"); message.success("已下发签字任务");
fetchDetail(); fetchDetail()
} }
}; };
const handleConfirm = async (resultCode) => { const handleConfirm = async (resultCode) => {
if(!rows.every((r) => r.reviewConclusion)){
message.error("请填写审核结论");
return;
}
return new Promise(async (resolve, reject) => {
if (!projectId) return; if (!projectId) return;
const payload = { const payload = {
id: internalReview?.id, id: internalReview?.id,
@ -109,12 +120,16 @@ const InternalReviewContent = (props) => {
}; };
const res = await props.evalInternalReviewConfirm(payload); const res = await props.evalInternalReviewConfirm(payload);
if (res?.success !== false) { if (res?.success !== false) {
message.success( message.success(resultCode === "passed" ? "已提交" : "已保存");
resultCode === "passed" ? "已提交" : "已保存", resolve(res);
); if (resultCode === "passed") {
fetchDetail();
props.getDetail?.(); props.getDetail?.();
} }
}
else{
reject();
}
});
}; };
const columns = [ const columns = [
@ -128,7 +143,9 @@ const InternalReviewContent = (props) => {
<Select <Select
value={val} value={val}
size="small" size="small"
style={{ width: 100 }} style={{ width: 130 }}
allowClear
placeholder="请选择结论"
onChange={(v) => updateRow(idx, "reviewConclusion", v)} onChange={(v) => updateRow(idx, "reviewConclusion", v)}
> >
{CONCLUSION_OPTIONS.map((opt) => ( {CONCLUSION_OPTIONS.map((opt) => (
@ -169,9 +186,20 @@ const InternalReviewContent = (props) => {
<Col span={8}> <Col span={8}>
<Card size="small"> <Card size="small">
<Typography.Text type="secondary">内审人</Typography.Text> <Typography.Text type="secondary">内审人</Typography.Text>
<div style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}> <div
<Tooltip title={(reviewers || []).map((r) => r.personnelName).join("、")}> style={{
<strong>{(reviewers || []).map((r) => r.personnelName).join("、") || "-"}</strong> overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
<Tooltip
title={(reviewers || []).map((r) => r.personnelName).join("、")}
>
<strong>
{(reviewers || []).map((r) => r.personnelName).join("、") ||
"-"}
</strong>
</Tooltip> </Tooltip>
</div> </div>
</Card> </Card>
@ -213,6 +241,9 @@ const InternalReviewContent = (props) => {
<TextArea <TextArea
rows={2} rows={2}
value={overallOpinion} value={overallOpinion}
allowClear
placeholder="请输入总体审核意见"
maxLength={120}
onChange={(e) => setOverallOpinion(e.target.value)} onChange={(e) => setOverallOpinion(e.target.value)}
style={{ marginTop: 4 }} style={{ marginTop: 4 }}
/> />
@ -237,7 +268,6 @@ const InternalReviewContent = (props) => {
onClick={handleAssign} onClick={handleAssign}
loading={internalReviewAssignLoading} loading={internalReviewAssignLoading}
disabled={isDispatched || !selectedReviewer} disabled={isDispatched || !selectedReviewer}
> >
{isDispatched ? "已下发APP签字" : "下发APP签字"} {isDispatched ? "已下发APP签字" : "下发APP签字"}
</Button> </Button>

View File

@ -21,6 +21,7 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace"; import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions"; import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions";
import { tools } from "@cqsjjb/jjb-common-lib"; import { tools } from "@cqsjjb/jjb-common-lib";
import AttachmentUpload from "~/components/AttachmentUpload";
const { router } = tools; const { router } = tools;
const { TextArea } = Input; const { TextArea } = Input;
@ -64,6 +65,7 @@ const RiskAnalysisContent = (props) => {
if (res?.success !== false && res?.data) { if (res?.success !== false && res?.data) {
form.setFieldsValue({ form.setFieldsValue({
...res.data, ...res.data,
riskFileUrl: res.data.riskFileUrl? JSON.parse(res.data.riskFileUrl) :undefined,
analysisDate: res.data.analysisDate analysisDate: res.data.analysisDate
? dayjs(res.data.analysisDate) ? dayjs(res.data.analysisDate)
: undefined, : undefined,
@ -96,6 +98,7 @@ const RiskAnalysisContent = (props) => {
...rest, ...rest,
projectId, projectId,
statusCode, statusCode,
riskFileUrl: rest.riskFileUrl? JSON.stringify(rest.riskFileUrl) :undefined,
analysisDate: analysisDate analysisDate: analysisDate
? analysisDate.format("YYYY-MM-DD") ? analysisDate.format("YYYY-MM-DD")
: undefined, : undefined,
@ -325,7 +328,7 @@ const RiskAnalysisContent = (props) => {
</div> </div>
</Card> </Card>
<Card type="inner" size="small" title="参与风险分析人员签字"> <Card type="inner" size="small" title="参与风险分析人员签字" className="pf-section">
<Form.List name="participants"> <Form.List name="participants">
{(fields, { add, remove }) => { {(fields, { add, remove }) => {
addParticipantRef.current = add; addParticipantRef.current = add;
@ -463,55 +466,14 @@ const RiskAnalysisContent = (props) => {
</Form.List> </Form.List>
</Card> </Card>
<Modal <AttachmentUpload
title="选择参与人员" name="riskFileUrl"
open={participantModalOpen} style={{ marginBottom: 0 }}
onCancel={() => setParticipantModalOpen(false)} label="风险分析表附件"
onOk={() => { accept=".pdf,.docx,.jpg,.png"
const selected = ( maxCount={10}
evalProjectDetailData?.participants || [] extra="支持 PDF、Word、JPG、PNG 格式。上传后可替换修改,系统保留当前归档文件信息"
).filter((p) => selectedParticipantIds.includes(p.id));
selected.forEach((p) => {
addParticipantRef.current?.({
personnelId: p.id,
personnelName: p.name,
deptName: p.deptName,
opinion: "",
projectId,
});
});
setParticipantModalOpen(false);
}}
>
<Table
dataSource={(() => {
const existing = (
form.getFieldValue("participants") || []
).map((p) => p.personnelName);
return (evalProjectDetailData?.participants || []).filter(
(p) => !existing.includes(p.name),
);
})()}
rowKey="id"
rowSelection={{
type: "checkbox",
selectedRowKeys: selectedParticipantIds,
onChange: (keys) => setSelectedParticipantIds(keys),
getCheckboxProps: (record) => ({
disabled: false,
}),
}}
columns={[
{ title: "姓名", dataIndex: "name", width: 100 },
{ title: "部门", dataIndex: "deptName", width: 120 },
{ title: "岗位", dataIndex: "posName", width: 120 },
{ title: "能力", dataIndex: "capacity", width: 120 },
]}
pagination={false}
size="small"
locale={{ emptyText: "所有人员均已添加" }}
/> />
</Modal>
</Form> </Form>
</Card> </Card>
@ -558,6 +520,56 @@ const RiskAnalysisContent = (props) => {
</div> </div>
</Card> </Card>
<Modal
title="选择参与人员"
open={participantModalOpen}
onCancel={() => setParticipantModalOpen(false)}
onOk={() => {
const selected = (evalProjectDetailData?.participants || []).filter(
(p) => selectedParticipantIds.includes(p.id),
);
selected.forEach((p) => {
addParticipantRef.current?.({
personnelId: p.id,
personnelName: p.name,
deptName: p.deptName,
opinion: "",
projectId,
});
});
setParticipantModalOpen(false);
}}
>
<Table
dataSource={(() => {
const existing = (form.getFieldValue("participants") || []).map(
(p) => p.personnelName,
);
return (evalProjectDetailData?.participants || []).filter(
(p) => !existing.includes(p.name),
);
})()}
rowKey="id"
rowSelection={{
type: "checkbox",
selectedRowKeys: selectedParticipantIds,
onChange: (keys) => setSelectedParticipantIds(keys),
getCheckboxProps: (record) => ({
disabled: false,
}),
}}
columns={[
{ title: "姓名", dataIndex: "name", width: 100 },
{ title: "部门", dataIndex: "deptName", width: 120 },
{ title: "岗位", dataIndex: "posName", width: 120 },
{ title: "能力", dataIndex: "capacity", width: 120 },
]}
pagination={false}
size="small"
locale={{ emptyText: "所有人员均已添加" }}
/>
</Modal>
<Flex justify="flex-end" gap={8}> <Flex justify="flex-end" gap={8}>
<Button <Button
onClick={() => handleSave(1)} onClick={() => handleSave(1)}
@ -565,7 +577,7 @@ const RiskAnalysisContent = (props) => {
> >
暂存 暂存
</Button> </Button>
<Button>打印记录表</Button>
<Button <Button
type="primary" type="primary"
onClick={() => handleSave(2)} onClick={() => handleSave(2)}

View File

@ -61,6 +61,13 @@ const NoticePanel = () => (
下载模板 下载模板
</Button> </Button>
</div> </div>
<AttachmentUpload
name="PRACTICE_NOTIFICATION"
label="从业告知书"
maxCount={1}
accept=".docx"
extra="仅支持上传 Word 格式文件"
/>
</div> </div>
); );
@ -111,19 +118,72 @@ const InspectionPanel = ({ dataSource = [], evalProjectDetailData = {} }) => {
<span>签到日期{dataSource?.[0]?.singInTime ? dayjs(dataSource?.[0]?.singInTime).format('YYYY-MM-DD') : '-'}</span> <span>签到日期{dataSource?.[0]?.singInTime ? dayjs(dataSource?.[0]?.singInTime).format('YYYY-MM-DD') : '-'}</span>
</div> </div>
</Card> </Card>
<AttachmentUpload
name="SITE_INSPECTION_FORM"
label="现场检查表"
maxCount={10}
accept=".pdf,.doc,.docx,.xls,.xlsx"
extra="检查表应包含项目名称检查日期检查人员检查依据逐项检查结果和不符合项记录上传后与人员打卡记录共同作为现场踏勘凭证
支持ExcelWordPDF和清晰扫描件"
/>
</div> </div>
); );
}; };
const SimpleUploadPanel = ({ index, title, desc }) => ( const RectificationNoticePanel = () => (
<div> <div>
<div className="pf-section-header"> <div className="pf-section-header">
<div> <div>
<span style={{ fontSize: 20, fontWeight: 600, color: "#1677ff", marginRight: 8 }}>{index}</span> <span style={{ fontSize: 20, fontWeight: 600, color: "#1677ff", marginRight: 8 }}>03</span>
<Typography.Text strong style={{ fontSize: 15 }}>{title}</Typography.Text> <Typography.Text strong style={{ fontSize: 15 }}>整改通知单上传</Typography.Text>
<Typography.Paragraph type="secondary" style={{ margin: 0 }}>{desc}</Typography.Paragraph> <Typography.Paragraph type="secondary" style={{ margin: 0 }}>本节点不在线编制整改通知只归档机构已出具的整改通知单文件上传成功即完成后续可替换修改</Typography.Paragraph>
</div> </div>
</div> </div>
<AttachmentUpload
name="RECTIFICATION_NOTICE"
label="整改通知单"
maxCount={10}
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png,.xls,.xlsx"
extra="支持 PDF、Word、JPG、PNG、Excel 格式,最多上传 10 个文件"
/>
</div>
);
const EnterpriseRectificationPanel = () => (
<div>
<div className="pf-section-header">
<div>
<span style={{ fontSize: 20, fontWeight: 600, color: "#1677ff", marginRight: 8 }}>04</span>
<Typography.Text strong style={{ fontSize: 15 }}>企业整改报告上传</Typography.Text>
<Typography.Paragraph type="secondary" style={{ margin: 0 }}>企业线下提交整改报告由机构人员上传归档本系统不提供企业在线提交和逐项整改流程</Typography.Paragraph>
</div>
</div>
<AttachmentUpload
name="ENTERPRISE_RECTIFICATION"
label="企业整改报告"
maxCount={10}
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png,.zip,.rar,.7z"
extra="支持 PDF、Word、压缩包、图片格式最多上传 10 个文件"
/>
</div>
);
const RectificationReviewPanel = () => (
<div>
<div className="pf-section-header">
<div>
<span style={{ fontSize: 20, fontWeight: 600, color: "#1677ff", marginRight: 8 }}>05</span>
<Typography.Text strong style={{ fontSize: 15 }}>整改复查单上传</Typography.Text>
<Typography.Paragraph type="secondary" style={{ margin: 0 }}>机构人员现场复查后将经双方签字确认的整改复查单上传归档上传后本节点自动标记完成</Typography.Paragraph>
</div>
</div>
<AttachmentUpload
name="RECTIFICATION_REVIEW"
label="整改情况复查单"
maxCount={10}
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png,.xls,.xlsx"
extra="支持 PDF、Word、JPG、PNG、Excel 格式,最多上传 10 个文件"
/>
</div> </div>
); );
@ -217,39 +277,22 @@ const SurveyContent = (props) => {
items={STAGES.map((s) => ({ title: s.label }))} items={STAGES.map((s) => ({ title: s.label }))}
/> />
{activeStage === "PRACTICE_NOTIFICATION" && <NoticePanel />}
{activeStage === "SITE_INSPECTION_FORM" && <InspectionPanel dataSource={surveyPersonnel} evalProjectDetailData={evalProjectDetailData} />}
{activeStage === "RECTIFICATION_NOTICE" && (
<SimpleUploadPanel
index="03" title="整改通知单上传"
desc="本节点不在线编制整改通知,只归档机构已出具的《整改通知单》文件;上传成功即完成,后续可替换修改。"
/>
)}
{activeStage === "ENTERPRISE_RECTIFICATION" && (
<SimpleUploadPanel
index="04" title="企业整改报告上传"
desc="企业线下提交《整改报告》后,由机构人员上传归档;本系统不提供企业在线提交和逐项整改流程。"
/>
)}
{activeStage === "RECTIFICATION_REVIEW" && (
<SimpleUploadPanel
index="05" title="整改复查单上传"
desc="机构人员现场复查后,将经双方签字确认的《整改复查单》上传归档;上传后本节点自动标记完成。"
/>
)}
<Form form={form} layout="vertical"> <Form form={form} layout="vertical">
{STAGES.map((s) => ( <div style={{ display: activeStage === "PRACTICE_NOTIFICATION" ? "block" : "none" }}>
<div key={s.key} style={{ display: s.key === activeStage ? "block" : "none" }}> <NoticePanel />
<AttachmentUpload </div>
name={s.key} <div style={{ display: activeStage === "SITE_INSPECTION_FORM" ? "block" : "none" }}>
label="上传附件" <InspectionPanel dataSource={surveyPersonnel} evalProjectDetailData={evalProjectDetailData} />
maxCount={10} </div>
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png,.xls,.xlsx" <div style={{ display: activeStage === "RECTIFICATION_NOTICE" ? "block" : "none" }}>
extra="支持 PDF、Word、JPG、PNG、Excel 格式,最多上传 10 个文件" <RectificationNoticePanel />
/> </div>
<div style={{ display: activeStage === "ENTERPRISE_RECTIFICATION" ? "block" : "none" }}>
<EnterpriseRectificationPanel />
</div>
<div style={{ display: activeStage === "RECTIFICATION_REVIEW" ? "block" : "none" }}>
<RectificationReviewPanel />
</div> </div>
))}
</Form> </Form>
<Flex justify="flex-end" gap={8} style={{ marginTop: 16 }}> <Flex justify="flex-end" gap={8} style={{ marginTop: 16 }}>

View File

@ -76,6 +76,8 @@ const TechnicalReviewContent = (props) => {
const handleAssign = async () => { const handleAssign = async () => {
if (!projectId) return; if (!projectId) return;
const result= await handleConfirm("not_passed");
if(!result) return;
const res = await props.evalTechReviewAssign({ const res = await props.evalTechReviewAssign({
projectId, projectId,
signTaskPersonCmd: { signTaskPersonCmd: {
@ -87,12 +89,17 @@ const TechnicalReviewContent = (props) => {
}); });
if (res?.success !== false) { if (res?.success !== false) {
message.success("已下发签字任务"); message.success("已下发签字任务");
fetchDetail(); fetchDetail()
} }
}; };
const handleConfirm = async (resultCode) => { const handleConfirm = async (resultCode) => {
if (!rows.every((r) => r.reviewConclusion)) {
message.error("请填写审核结论");
return;
}
if (!projectId) return; if (!projectId) return;
return new Promise(async (resolve, reject) => {
const payload = { const payload = {
id: techReview?.id, id: techReview?.id,
projectId, projectId,
@ -110,12 +117,17 @@ const TechnicalReviewContent = (props) => {
}; };
const res = await props.evalTechReviewConfirm(payload); const res = await props.evalTechReviewConfirm(payload);
if (res?.success !== false) { if (res?.success !== false) {
resolve(res);
message.success( message.success(
resultCode === "1" ? "已确认技审并提交过程控制" : "已保存", resultCode === "passed" ? "已确认技审并提交过程控制" : "已保存",
); );
fetchDetail(); if (resultCode === "passed") {
props.getDetail?.(); props.getDetail?.();
} }
} else {
reject();
}
});
}; };
const columns = [ const columns = [
@ -129,7 +141,9 @@ const TechnicalReviewContent = (props) => {
<Select <Select
value={val} value={val}
size="small" size="small"
style={{ width: 100 }} style={{ width: 130 }}
allowClear
placeholder="请选择结论"
onChange={(v) => updateRow(idx, "reviewConclusion", v)} onChange={(v) => updateRow(idx, "reviewConclusion", v)}
> >
{CONCLUSION_OPTIONS.map((opt) => ( {CONCLUSION_OPTIONS.map((opt) => (
@ -228,6 +242,9 @@ const TechnicalReviewContent = (props) => {
<TextArea <TextArea
rows={2} rows={2}
value={overallOpinion} value={overallOpinion}
allowClear
placeholder="请输入技术审核意见"
maxLength={120}
onChange={(e) => setOverallOpinion(e.target.value)} onChange={(e) => setOverallOpinion(e.target.value)}
style={{ marginTop: 4 }} style={{ marginTop: 4 }}
/> />
@ -259,10 +276,15 @@ const TechnicalReviewContent = (props) => {
</Flex> </Flex>
<Flex justify="flex-end" gap={8} style={{ marginTop: 16 }}> <Flex justify="flex-end" gap={8} style={{ marginTop: 16 }}>
<Button onClick={() => handleConfirm("2")}>保存意见</Button> <Button
loading={props.safetyEvalBusiness?.techReviewConfirmLoading}
onClick={() => handleConfirm("not_passed")}
>
保存意见
</Button>
<Button <Button
type="primary" type="primary"
onClick={() => handleConfirm("1")} onClick={() => handleConfirm("passed")}
loading={props.safetyEvalBusiness?.techReviewConfirmLoading} loading={props.safetyEvalBusiness?.techReviewConfirmLoading}
> >
确认技审并提交过程控制 确认技审并提交过程控制

View File

@ -61,7 +61,7 @@ export default class Container extends React.Component {
function AppMiddle(props) { function AppMiddle(props) {
const content = process.env.NODE_ENV === "development" && !window.__POWERED_BY_QIANKUN__ && props.location.pathname !== "/container/Driver" const content = process.env.NODE_ENV === "development" && !window.__POWERED_BY_QIANKUN__ && props.location.pathname !== "/container/Driver"
? <SimulatedLayout>{props.children}</SimulatedLayout> ? <SimulatedLayout history={props.history}>{props.children}</SimulatedLayout>
: props.children; : props.children;
return ( return (