dev-tmp1
tangjie 2026-08-21 11:17:29 +08:00
parent 1483fceac3
commit 7ad453f446
10 changed files with 109 additions and 142 deletions

View File

@ -12,9 +12,9 @@ module.exports = {
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095 // 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
//API_HOST: "http://localhost:80", //API_HOST: "http://localhost:80",
API_HOST: "http://192.168.0.134", // API_HOST: "http://192.168.0.134",
// API_HOST: "http://192.168.0.150", //太浅 // API_HOST: "http://192.168.0.150", //太浅
// API_HOST: "https://gbs-gateway.qhdsafety.com", API_HOST: "https://gbs-gateway.qhdsafety.com",
// API_HOST: "http://192.168.0.103", //huwei // API_HOST: "http://192.168.0.103", //huwei
}, },
production: { production: {

View File

@ -1,35 +0,0 @@
姓名
{userName}
性别
{gender}
出生日期
{birthDate}
现住址
{currentAddress}
办公地址
{officeAddress}
职业资格等级及证书号码
{qualificationText}
注册安全工程师证书编号
{certNo}
身份证件号码
{idCardNo}
联系电话
{phone}
学历学位
{education}
毕业院校及专业
{schoolMajor}
出版学术专著、专利、科技发明及获奖情况
{publications}
自我申报的专业能力及认定方式
{capability}
申报专业能力证明材料
{certFiles}
主要学习工作经历
{workExperience}
本人签字
从业机构确认

View File

@ -0,0 +1,44 @@
安全评价师简历表
姓 名
{userName}
性 别
{gender}
出生日期
{birthDate}
现住址
{currentAddress}
办公地址
{officeAddress}
职业资格等级及证书号码
{qualificationText}
注册安全工程师证书编号
{certNo}
身份证件号码
{idCardNo}
联系电话
{phone}
学 历
学 位
全日制教育
{education}
毕业院校及专业
{schoolMajor}
在职教育
{ZYeducation}
毕业院校及专业
{ZYschoolMajor}
出版学术专著、专利、科技发明、获得科技进步奖、发表学术论文等情况
{publications}
自我申报的专业能力及认定方式
{capability}
申报专业能力证明材料
{certFiles}
主要学习工作经历:
{workExperience}
签字:
从业机构确认意见(盖章)
年 月 日
1.专职安全评价师应全部填写2.专业能力证明材料根据《安全评价师专业能力对照表》的认定要求填报3.学习简历从大学开始(包括大学毕业后的继续教育)。

View File

@ -9,7 +9,7 @@ import {
} from "~/enumerate/constant"; } from "~/enumerate/constant";
import "./index.less"; import "./index.less";
const RESUME_TEMPLATE_URL = "https://test-dragon-yf-pub.oss-cn-hangzhou.aliyuncs.com/jjb/6a86d212e4b072649bfb8a6e.docx"; const RESUME_TEMPLATE_URL = "https://test-dragon-yf-pub.oss-cn-hangzhou.aliyuncs.com/jjb/6a87b61be4b063db1579e790.docx";
const GENDER_MAP = { 1: "男", 2: "女" }; const GENDER_MAP = { 1: "男", 2: "女" };
@ -93,6 +93,8 @@ export default function StaffResumeModal({
const regCert = info.registeredSafetyEngineerCert; const regCert = info.registeredSafetyEngineerCert;
const regCertFiles = parseFileList(regCert?.certAttachmentUrl); const regCertFiles = parseFileList(regCert?.certAttachmentUrl);
const isZyEducation = info.educationTypeName === "在职教育";
const educationText = [info.educationTypeName, info.educationLevelName] const educationText = [info.educationTypeName, info.educationLevelName]
.filter(Boolean) .filter(Boolean)
.join("") || "-"; .join("") || "-";
@ -132,8 +134,11 @@ export default function StaffResumeModal({
certNo: display(regCert?.certNo), certNo: display(regCert?.certNo),
idCardNo: maskIdCard(info.idCardNo), idCardNo: maskIdCard(info.idCardNo),
phone: maskPhone(info.account), phone: maskPhone(info.account),
education: educationText, // 全日制填 education/schoolMajor在职教育填 ZYeducation/ZYschoolMajor
schoolMajor: schoolMajorText, education: isZyEducation ? "" : info.educationLevelName,
schoolMajor: isZyEducation ? "" : schoolMajorText,
ZYeducation: isZyEducation ? info.educationLevelName : "",
ZYschoolMajor: isZyEducation ? schoolMajorText : "",
publications: info.publications || "无", publications: info.publications || "无",
capability: capabilityText, capability: capabilityText,
certFiles: certFiles:

View File

@ -24,6 +24,7 @@ import { PROJECT_TYPE_MAP } from "~/enumerate/constant";
import WpsEditor from "~/pages/Container/WpsEditor"; import WpsEditor from "~/pages/Container/WpsEditor";
import RiskAnalysisContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/RiskAnalysisContent"; import RiskAnalysisContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/RiskAnalysisContent";
import InternalReviewContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/InternalReviewContent"; import InternalReviewContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/InternalReviewContent";
import TeamContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/TeamContent";
import TechnicalReviewContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/TechnicalReviewContent"; import TechnicalReviewContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/TechnicalReviewContent";
import ControlContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/ControlContent"; import ControlContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/ControlContent";
import ArchiveContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent"; import ArchiveContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent";
@ -153,7 +154,7 @@ const ContractPanel = ({ data, loading }) => {
<Card <Card
type="inner" type="inner"
size="small" size="small"
title="合同扫描件" title="项目合同扫描件"
className="epm-detail-section" className="epm-detail-section"
> >
{scanFiles.map((file, idx) => renderFile(file, idx))} {scanFiles.map((file, idx) => renderFile(file, idx))}
@ -163,76 +164,6 @@ const ContractPanel = ({ data, loading }) => {
); );
}; };
// ===== 项目组人员面板 =====
const TeamPanel = ({ data, loading }) => {
if (!data || data.length === 0)
return <Empty description="暂无项目组人员数据" />;
const columns = [
{ title: "姓名", dataIndex: "personnelName", width: 100 },
{ title: "部门/岗位", dataIndex: "deptName", width: 160, ellipsis: true },
{
title: "项目角色",
dataIndex: "role",
width: 200,
render: (roles) =>
(Array.isArray(roles) ? roles : [roles])
.map((r) => ROLE_DEFINITIONS_MAP[r] || r)
.join("、"),
},
{
title: "资质与专业",
dataIndex: "capacity",
width: 200,
ellipsis: true,
},
{
title: "主要职责",
dataIndex: "responsibility",
ellipsis: true,
},
];
let leader=null;
let processLeader=null;
data.forEach((item) => {
if (item.role.includes('PROJECT_LEAD')) {
leader = item.personnelName;
}
if (item.role.includes('PROCESS_CONTROL_LEAD')) {
processLeader = item.personnelName;
}
});
return <>
<div className="epm-detail-band">
<div className="epm-detail-band-item">
<span>项目组人数</span>
<strong>
{data.length || 0}
</strong>
</div>
<div className="epm-detail-band-item">
<span>项目负责人</span>
<strong>{leader || "-"}</strong>
</div>
<div className="epm-detail-band-item">
<span>过程控制负责人</span>
<strong>
{processLeader || "-"}
</strong>
</div>
</div>
<Table
dataSource={data}
columns={columns}
rowKey="personnelId"
pagination={false}
size="small"
scroll={{ x: 800 }}
/>
</>
};
// ===== 踏勘情况面板 ===== // ===== 踏勘情况面板 =====
const SurveyPanel = ({ personnel, attachments, loading, detailData }) => { const SurveyPanel = ({ personnel, attachments, loading, detailData }) => {
@ -394,7 +325,7 @@ const EvalProjectMonitorDetail = (props) => {
const [teamMembers, setTeamMembers] = useState([]); const [teamMembers, setTeamMembers] = useState([]);
const [surveyPersonnel, setSurveyPersonnel] = useState([]); const [surveyPersonnel, setSurveyPersonnel] = useState([]);
const [surveyAttachments, setSurveyAttachments] = useState({}); const [surveyAttachments, setSurveyAttachments] = useState({});
const [activeKey, setActiveKey] = useState("BUSINESS_CONTRACT"); const [activeKey, setActiveKey] = useState("RISK_ANALYSIS");
const [wpsVisible, setWpsVisible] = useState(false); const [wpsVisible, setWpsVisible] = useState(false);
const fetchContract = async () => { const fetchContract = async () => {
@ -405,13 +336,6 @@ const EvalProjectMonitorDetail = (props) => {
} }
}; };
const fetchTeam = async () => {
if (!projectId) return;
const res = await evalProjectMemberPage({ projectId });
if (res?.success !== false && res?.data) {
setTeamMembers(res.data);
}
};
const fetchSurvey = async () => { const fetchSurvey = async () => {
if (!projectId) return; if (!projectId) return;
@ -437,7 +361,7 @@ const EvalProjectMonitorDetail = (props) => {
getDocByProjectId({ projectId }); getDocByProjectId({ projectId });
} }
fetchContract(); fetchContract();
fetchTeam();
fetchSurvey(); fetchSurvey();
}, []); }, []);
@ -521,10 +445,11 @@ const EvalProjectMonitorDetail = (props) => {
); );
return ( return (
<PageLayout history={props.history} previous title="安评项目监管详情"> <PageLayout history={props.history} previous title="安评项目监管详情" footer={<div id="pf-footer-slot" />}>
{projectInfo} {projectInfo}
<Tabs <Tabs
activeKey={activeKey} activeKey={activeKey}
destroyOnHidden
onChange={(key) => { onChange={(key) => {
if (key === "REPORT_PREPARATION") { if (key === "REPORT_PREPARATION") {
if (!wpsDoc?.fileId) { if (!wpsDoc?.fileId) {
@ -552,7 +477,7 @@ const EvalProjectMonitorDetail = (props) => {
{ {
key: "PROJECT_GROUP", key: "PROJECT_GROUP",
label: renderTabLabel("项目组人员", "PROJECT_GROUP"), label: renderTabLabel("项目组人员", "PROJECT_GROUP"),
children: <TeamPanel data={teamMembers} loading={memberLoading} />, children: <TeamContent readOnly />,
}, },
{ {
key: "SITE_SURVEY", key: "SITE_SURVEY",
@ -591,16 +516,7 @@ const EvalProjectMonitorDetail = (props) => {
disabled: controlLocked, disabled: controlLocked,
children: <ControlContent readOnly />, children: <ControlContent readOnly />,
}, },
{
key: "ARCHIVE",
label: renderLockableTabLabel(
"项目归档",
evalProjectDetailData?.archiveFlag === 1,
archiveLocked,
),
disabled: archiveLocked,
children: <ArchiveContent readOnly />,
},
]} ]}
/> />
{wpsVisible && ( {wpsVisible && (

View File

@ -178,15 +178,15 @@ const ControlContent = ({ readOnly, ...props }) => {
]; ];
const reviewerSignatureTaskStatusMap = { const reviewerSignatureTaskStatusMap = {
dispatched: "已下发", dispatched: "待APP签字",
non_dispatched: "过程控制签字提交", non_dispatched: "过程控制签字提交",
completed: "已完成", completed: "已手写签字",
} }
const OrgHeadSignatureTaskStatusMap = { const OrgHeadSignatureTaskStatusMap = {
dispatched: "已下发", dispatched: "待APP签字",
non_dispatched: "下发APP签字", non_dispatched: "下发APP签字",
completed: "已完成", completed: "已手写签字",
} }
return ( return (
@ -221,7 +221,6 @@ const ControlContent = ({ readOnly, ...props }) => {
<Card <Card
title="过程控制审核" title="过程控制审核"
style={{ marginBottom: 16 }} style={{ marginBottom: 16 }}
styles={{ body: { paddingBottom: 0 } }}
> >
<Table <Table
dataSource={rows} dataSource={rows}
@ -236,7 +235,7 @@ const ControlContent = ({ readOnly, ...props }) => {
</div> </div>
<Image src={processControl?.reviewerSignatureUrl} width={120} height={80} /> <Image src={processControl?.reviewerSignatureUrl} width={120} height={80} />
</div>} </div>}
<Collapse {!readOnly&&<Collapse
style={{ marginTop: 12 }} style={{ marginTop: 12 }}
items={[ items={[
{ {
@ -273,7 +272,7 @@ const ControlContent = ({ readOnly, ...props }) => {
), ),
}, },
]} ]}
/> />}
</Card> </Card>
)} )}

View File

@ -12,6 +12,8 @@ import {
Col, Col,
message, message,
Tooltip, Tooltip,
Popover,
Image,
Collapse, Collapse,
} from "antd"; } from "antd";
import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Connect } from "@cqsjjb/jjb-dva-runtime";
@ -76,8 +78,8 @@ const InternalReviewContent = ({ readOnly, ...props }) => {
const isDispatched = ["dispatched", 'completed'].includes(internalReview?.reviewerSignatureTaskStatus); const isDispatched = ["dispatched", 'completed'].includes(internalReview?.reviewerSignatureTaskStatus);
const textMap={ const textMap={
"dispatched": "已下发", "dispatched": "待APP签字",
"completed": "已完成", "completed": "已手写签字",
}; };
const problemCount = rows.filter( const problemCount = rows.filter(
@ -273,17 +275,40 @@ const InternalReviewContent = ({ readOnly, ...props }) => {
label: r.personnelName, label: r.personnelName,
}))} }))}
/> />
<Button {(() => {
size="small" const btn = (
onClick={handleAssign} <Button
loading={internalReviewAssignLoading || props.safetyEvalBusiness?.internalReviewConfirmLoading} size="small"
disabled={isDispatched || !selectedReviewer || readOnly} onClick={handleAssign}
> loading={internalReviewAssignLoading || props.safetyEvalBusiness?.internalReviewConfirmLoading}
{textMap[internalReview?.reviewerSignatureTaskStatus] || '下发App签字'} disabled={isDispatched || !selectedReviewer || readOnly}
</Button> >
{textMap[internalReview?.reviewerSignatureTaskStatus] || '发起APP签字'}
</Button>
);
// 已签字且有签字图片时悬停展示签字图片disabled 按钮不触发 hover需用 span 包裹)
if (internalReview?.reviewerSignatureUrl) {
return (
<Popover
placement="topRight"
content={
<Image
className="pf-sign-img"
src={internalReview.reviewerSignatureUrl}
alt="签字图片"
/>
}
>
<span>{btn}</span>
</Popover>
);
}
return btn;
})()}
</Flex> </Flex>
</Flex> </Flex>
<Collapse {!readOnly &&<Collapse
style={{marginTop: '12px'}} style={{marginTop: '12px'}}
items={[ items={[
{ {
@ -316,7 +341,7 @@ const InternalReviewContent = ({ readOnly, ...props }) => {
), ),
}, },
]} ]}
/> />}
{!readOnly && ( {!readOnly && (
<LayoutFooterPortal slotId="pf-footer-slot"> <LayoutFooterPortal slotId="pf-footer-slot">
<Flex justify="flex-end" gap={8}> <Flex justify="flex-end" gap={8}>

View File

@ -566,21 +566,20 @@ const RiskAnalysisContent = ({ readOnly, ...props }) => {
return btn; return btn;
}, },
}, },
{ !readOnly && {
title: "操作", title: "操作",
render: (_, field) => ( render: (_, field) => (
<Button <Button
type="link" type="link"
danger danger
size="small" size="small"
disabled={readOnly}
onClick={() => remove(field.name)} onClick={() => remove(field.name)}
> >
删除 删除
</Button> </Button>
), ),
}, },
]} ].filter(Boolean)}
rowKey={(_, index) => index} rowKey={(_, index) => index}
pagination={false} pagination={false}
/> />

View File

@ -360,7 +360,7 @@ const TeamContent = ({ readOnly, ...props }) => {
ellipsis: true, ellipsis: true,
render: (roles) => roles.map((r) => r.duty).join(""), render: (roles) => roles.map((r) => r.duty).join(""),
}, },
{ !readOnly && {
title: "操作", title: "操作",
width: 80, width: 80,
fixed: "right", fixed: "right",
@ -369,14 +369,13 @@ const TeamContent = ({ readOnly, ...props }) => {
type="link" type="link"
danger danger
size="small" size="small"
disabled={readOnly}
onClick={() => handleRemovePerson(record.personnelId)} onClick={() => handleRemovePerson(record.personnelId)}
> >
移除 移除
</Button> </Button>
), ),
}, },
]; ].filter(Boolean);
// 参与风险分析人员签字(去掉分析意见、只能选择一个人) // 参与风险分析人员签字(去掉分析意见、只能选择一个人)
const signerColumns = [ const signerColumns = [
@ -422,7 +421,7 @@ const TeamContent = ({ readOnly, ...props }) => {
return btn; return btn;
}, },
}, },
{ !readOnly && {
title: "操作", title: "操作",
width: 80, width: 80,
render: (_, record) => ( render: (_, record) => (
@ -430,14 +429,13 @@ const TeamContent = ({ readOnly, ...props }) => {
type="link" type="link"
danger danger
size="small" size="small"
disabled={readOnly}
onClick={() => handleRemoveSigner(record.personnelId)} onClick={() => handleRemoveSigner(record.personnelId)}
> >
删除 删除
</Button> </Button>
), ),
}, },
]; ].filter(Boolean);
return ( return (
<Spin spinning={memberLoading || memberParticipantListLoading}> <Spin spinning={memberLoading || memberParticipantListLoading}>
@ -535,7 +533,7 @@ const TeamContent = ({ readOnly, ...props }) => {
)} )}
</Card> </Card>
<details className="pf-role-guide"> {!readOnly &&<details className="pf-role-guide">
<summary className="pf-role-guide-title"> <summary className="pf-role-guide-title">
查看11类项目角色职责说明 查看11类项目角色职责说明
</summary> </summary>
@ -554,19 +552,19 @@ const TeamContent = ({ readOnly, ...props }) => {
</div> </div>
))} ))}
</div> </div>
</details> </details>}
{!readOnly && ( { (
<LayoutFooterPortal slotId="pf-footer-slot"> <LayoutFooterPortal slotId="pf-footer-slot">
<Flex justify="flex-end" gap={8}> <Flex justify="flex-end" gap={8}>
<Button onClick={handleExport}>导出Word</Button> <Button onClick={handleExport}>导出Word</Button>
<Button {!readOnly &&<Button
type="primary" type="primary"
loading={memberSaveLoading || memberParticipantSaveLoading} loading={memberSaveLoading || memberParticipantSaveLoading}
onClick={handleSave} onClick={handleSave}
> >
确认项目组 确认项目组
</Button> </Button>}
</Flex> </Flex>
</LayoutFooterPortal> </LayoutFooterPortal>
)} )}

View File

@ -12,6 +12,8 @@ import {
Typography, Typography,
message, message,
Tooltip, Tooltip,
Popover,
Image,
Collapse, Collapse,
} from "antd"; } from "antd";
import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Connect } from "@cqsjjb/jjb-dva-runtime";
@ -74,8 +76,8 @@ const TechnicalReviewContent = ({ readOnly, ...props }) => {
const isDispatched = ["dispatched", 'completed'].includes(techReview?.reviewerSignatureTaskStatus); const isDispatched = ["dispatched", 'completed'].includes(techReview?.reviewerSignatureTaskStatus);
const textMap={ const textMap={
"dispatched": "已下发", "dispatched": "待APP签字",
"completed": "已完成", "completed": "已手写签字",
}; };
const handleAssign = async () => { const handleAssign = async () => {
@ -272,17 +274,40 @@ const TechnicalReviewContent = ({ readOnly, ...props }) => {
label: r.personnelName, label: r.personnelName,
}))} }))}
/> />
<Button {(() => {
size="small" const btn = (
onClick={handleAssign} <Button
loading={props.safetyEvalBusiness?.techReviewAssignLoading || props.safetyEvalBusiness?.techReviewConfirmLoading} size="small"
disabled={isDispatched || !selectedReviewer || readOnly} onClick={handleAssign}
> loading={props.safetyEvalBusiness?.techReviewAssignLoading || props.safetyEvalBusiness?.techReviewConfirmLoading}
{textMap[techReview?.reviewerSignatureTaskStatus] || '下发App签字'} disabled={isDispatched || !selectedReviewer || readOnly}
</Button> >
{textMap[techReview?.reviewerSignatureTaskStatus] || '发起APP签字'}
</Button>
);
// 已签字且有签字图片时悬停展示签字图片disabled 按钮不触发 hover需用 span 包裹)
if (techReview?.reviewerSignatureUrl) {
return (
<Popover
placement="topRight"
content={
<Image
className="pf-sign-img"
src={techReview.reviewerSignatureUrl}
alt="签字图片"
/>
}
>
<span>{btn}</span>
</Popover>
);
}
return btn;
})()}
</Flex> </Flex>
</Flex> </Flex>
<Collapse {!readOnly &&<Collapse
style={{ marginTop: "12px" }} style={{ marginTop: "12px" }}
items={[ items={[
{ {
@ -315,7 +340,7 @@ const TechnicalReviewContent = ({ readOnly, ...props }) => {
), ),
}, },
]} ]}
/> />}
{!readOnly && ( {!readOnly && (
<LayoutFooterPortal slotId="pf-footer-slot"> <LayoutFooterPortal slotId="pf-footer-slot">
<Flex justify="flex-end" gap={8}> <Flex justify="flex-end" gap={8}>