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

# Conflicts:
#	jjb.config.js
dev-tmp1
luotaiqian 2026-08-21 14:43:26 +08:00
commit c0a0261561
10 changed files with 110 additions and 143 deletions

View File

@ -12,9 +12,9 @@ module.exports = {
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
//API_HOST: "http://localhost:80",
// API_HOST: "http://192.168.0.134",
API_HOST: "http://192.168.0.150", //太浅
// API_HOST: "https://gbs-gateway.qhdsafety.com",
// API_HOST: "http://192.168.0.134",
// API_HOST: "http://192.168.0.150", //太浅
API_HOST: "https://gbs-gateway.qhdsafety.com",
// API_HOST: "http://192.168.0.103", //huwei
},
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";
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: "女" };
@ -93,6 +93,8 @@ export default function StaffResumeModal({
const regCert = info.registeredSafetyEngineerCert;
const regCertFiles = parseFileList(regCert?.certAttachmentUrl);
const isZyEducation = info.educationTypeName === "在职教育";
const educationText = [info.educationTypeName, info.educationLevelName]
.filter(Boolean)
.join("") || "-";
@ -132,8 +134,11 @@ export default function StaffResumeModal({
certNo: display(regCert?.certNo),
idCardNo: maskIdCard(info.idCardNo),
phone: maskPhone(info.account),
education: educationText,
schoolMajor: schoolMajorText,
// 全日制填 education/schoolMajor在职教育填 ZYeducation/ZYschoolMajor
education: isZyEducation ? "" : info.educationLevelName,
schoolMajor: isZyEducation ? "" : schoolMajorText,
ZYeducation: isZyEducation ? info.educationLevelName : "",
ZYschoolMajor: isZyEducation ? schoolMajorText : "",
publications: info.publications || "无",
capability: capabilityText,
certFiles:

View File

@ -24,6 +24,7 @@ import { PROJECT_TYPE_MAP } from "~/enumerate/constant";
import WpsEditor from "~/pages/Container/WpsEditor";
import RiskAnalysisContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/RiskAnalysisContent";
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 ControlContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/ControlContent";
import ArchiveContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent";
@ -153,7 +154,7 @@ const ContractPanel = ({ data, loading }) => {
<Card
type="inner"
size="small"
title="合同扫描件"
title="项目合同扫描件"
className="epm-detail-section"
>
{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 }) => {
@ -394,7 +325,7 @@ const EvalProjectMonitorDetail = (props) => {
const [teamMembers, setTeamMembers] = useState([]);
const [surveyPersonnel, setSurveyPersonnel] = useState([]);
const [surveyAttachments, setSurveyAttachments] = useState({});
const [activeKey, setActiveKey] = useState("BUSINESS_CONTRACT");
const [activeKey, setActiveKey] = useState("RISK_ANALYSIS");
const [wpsVisible, setWpsVisible] = useState(false);
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 () => {
if (!projectId) return;
@ -437,7 +361,7 @@ const EvalProjectMonitorDetail = (props) => {
getDocByProjectId({ projectId });
}
fetchContract();
fetchTeam();
fetchSurvey();
}, []);
@ -521,10 +445,11 @@ const EvalProjectMonitorDetail = (props) => {
);
return (
<PageLayout history={props.history} previous title="安评项目监管详情">
<PageLayout history={props.history} previous title="安评项目监管详情" footer={<div id="pf-footer-slot" />}>
{projectInfo}
<Tabs
activeKey={activeKey}
destroyOnHidden
onChange={(key) => {
if (key === "REPORT_PREPARATION") {
if (!wpsDoc?.fileId) {
@ -552,7 +477,7 @@ const EvalProjectMonitorDetail = (props) => {
{
key: "PROJECT_GROUP",
label: renderTabLabel("项目组人员", "PROJECT_GROUP"),
children: <TeamPanel data={teamMembers} loading={memberLoading} />,
children: <TeamContent readOnly />,
},
{
key: "SITE_SURVEY",
@ -591,16 +516,7 @@ const EvalProjectMonitorDetail = (props) => {
disabled: controlLocked,
children: <ControlContent readOnly />,
},
{
key: "ARCHIVE",
label: renderLockableTabLabel(
"项目归档",
evalProjectDetailData?.archiveFlag === 1,
archiveLocked,
),
disabled: archiveLocked,
children: <ArchiveContent readOnly />,
},
]}
/>
{wpsVisible && (

View File

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

View File

@ -12,6 +12,8 @@ import {
Col,
message,
Tooltip,
Popover,
Image,
Collapse,
} from "antd";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
@ -76,8 +78,8 @@ const InternalReviewContent = ({ readOnly, ...props }) => {
const isDispatched = ["dispatched", 'completed'].includes(internalReview?.reviewerSignatureTaskStatus);
const textMap={
"dispatched": "已下发",
"completed": "已完成",
"dispatched": "待APP签字",
"completed": "已手写签字",
};
const problemCount = rows.filter(
@ -273,17 +275,40 @@ const InternalReviewContent = ({ readOnly, ...props }) => {
label: r.personnelName,
}))}
/>
<Button
size="small"
onClick={handleAssign}
loading={internalReviewAssignLoading || props.safetyEvalBusiness?.internalReviewConfirmLoading}
disabled={isDispatched || !selectedReviewer || readOnly}
>
{textMap[internalReview?.reviewerSignatureTaskStatus] || '下发App签字'}
</Button>
{(() => {
const btn = (
<Button
size="small"
onClick={handleAssign}
loading={internalReviewAssignLoading || props.safetyEvalBusiness?.internalReviewConfirmLoading}
disabled={isDispatched || !selectedReviewer || readOnly}
>
{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>
<Collapse
{!readOnly &&<Collapse
style={{marginTop: '12px'}}
items={[
{
@ -316,7 +341,7 @@ const InternalReviewContent = ({ readOnly, ...props }) => {
),
},
]}
/>
/>}
{!readOnly && (
<LayoutFooterPortal slotId="pf-footer-slot">
<Flex justify="flex-end" gap={8}>

View File

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

View File

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

View File

@ -12,6 +12,8 @@ import {
Typography,
message,
Tooltip,
Popover,
Image,
Collapse,
} from "antd";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
@ -74,8 +76,8 @@ const TechnicalReviewContent = ({ readOnly, ...props }) => {
const isDispatched = ["dispatched", 'completed'].includes(techReview?.reviewerSignatureTaskStatus);
const textMap={
"dispatched": "已下发",
"completed": "已完成",
"dispatched": "待APP签字",
"completed": "已手写签字",
};
const handleAssign = async () => {
@ -272,17 +274,40 @@ const TechnicalReviewContent = ({ readOnly, ...props }) => {
label: r.personnelName,
}))}
/>
<Button
size="small"
onClick={handleAssign}
loading={props.safetyEvalBusiness?.techReviewAssignLoading || props.safetyEvalBusiness?.techReviewConfirmLoading}
disabled={isDispatched || !selectedReviewer || readOnly}
>
{textMap[techReview?.reviewerSignatureTaskStatus] || '下发App签字'}
</Button>
{(() => {
const btn = (
<Button
size="small"
onClick={handleAssign}
loading={props.safetyEvalBusiness?.techReviewAssignLoading || props.safetyEvalBusiness?.techReviewConfirmLoading}
disabled={isDispatched || !selectedReviewer || readOnly}
>
{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>
<Collapse
{!readOnly &&<Collapse
style={{ marginTop: "12px" }}
items={[
{
@ -315,7 +340,7 @@ const TechnicalReviewContent = ({ readOnly, ...props }) => {
),
},
]}
/>
/>}
{!readOnly && (
<LayoutFooterPortal slotId="pf-footer-slot">
<Flex justify="flex-end" gap={8}>