2026-07-07 15:27:25 +08:00
|
|
|
import { Button, Descriptions, Modal, Table, Upload } from "antd";
|
2026-06-29 11:59:34 +08:00
|
|
|
import { useEffect, useRef, useState } from "react";
|
2026-06-26 17:28:06 +08:00
|
|
|
import FilePreviewModal, { FilePreviewContent } from "~/components/FilePreviewModal";
|
2026-06-29 11:59:34 +08:00
|
|
|
import { resolvePreviewSrc } from "~/components/CertPreviewImg";
|
2026-06-26 17:28:06 +08:00
|
|
|
import { fetchStaffCertDetail, fetchStaffCertListByPersonnelId } from "~/api/staffCertificate";
|
2026-07-02 16:58:07 +08:00
|
|
|
import { fetchOrgPersonnelDetail } from "~/api/qualFiling/personnelHelper";
|
2026-06-26 17:28:06 +08:00
|
|
|
|
|
|
|
|
const GENDER_MAP = { 1: "男", 2: "女" };
|
|
|
|
|
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
|
|
|
|
|
|
|
|
|
|
export default function StaffViewModal({
|
|
|
|
|
open,
|
|
|
|
|
currentId,
|
2026-07-02 16:58:07 +08:00
|
|
|
requestDetails = fetchOrgPersonnelDetail,
|
2026-06-26 17:28:06 +08:00
|
|
|
requestCertList = fetchStaffCertListByPersonnelId,
|
|
|
|
|
requestCertDetails = fetchStaffCertDetail,
|
|
|
|
|
onCancel,
|
|
|
|
|
}) {
|
|
|
|
|
const [info, setInfo] = useState({});
|
|
|
|
|
const [certificates, setCertificates] = useState([]);
|
|
|
|
|
const [detailLoading, setDetailLoading] = useState(false);
|
|
|
|
|
const [attachmentPreview, setAttachmentPreview] = useState(null);
|
|
|
|
|
const [certPreview, setCertPreview] = useState(null);
|
|
|
|
|
const [certPreviewLoading, setCertPreviewLoading] = useState(false);
|
|
|
|
|
const [certPreviewInfo, setCertPreviewInfo] = useState(null);
|
2026-06-29 11:59:34 +08:00
|
|
|
const requestDetailsRef = useRef(requestDetails);
|
|
|
|
|
const requestCertListRef = useRef(requestCertList);
|
|
|
|
|
const requestCertDetailsRef = useRef(requestCertDetails);
|
|
|
|
|
requestDetailsRef.current = requestDetails;
|
|
|
|
|
requestCertListRef.current = requestCertList;
|
|
|
|
|
requestCertDetailsRef.current = requestCertDetails;
|
2026-06-26 17:28:06 +08:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!open || !currentId) {
|
|
|
|
|
setInfo({});
|
|
|
|
|
setCertificates([]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
setDetailLoading(true);
|
2026-07-07 09:07:19 +08:00
|
|
|
|
2026-06-29 11:59:34 +08:00
|
|
|
const loadCertList = requestCertListRef.current;
|
2026-06-26 17:28:06 +08:00
|
|
|
Promise.all([
|
2026-07-07 09:07:19 +08:00
|
|
|
Promise.resolve(requestDetailsRef.current({ id: currentId })).catch((err) => {
|
|
|
|
|
console.warn("[StaffViewModal] load detail failed:", err);
|
|
|
|
|
return { data: {} };
|
|
|
|
|
}),
|
2026-06-29 11:59:34 +08:00
|
|
|
typeof loadCertList === "function"
|
|
|
|
|
? Promise.resolve(loadCertList(currentId)).catch((err) => {
|
2026-06-26 17:28:06 +08:00
|
|
|
console.warn("[StaffViewModal] load certificates failed:", err);
|
|
|
|
|
return [];
|
|
|
|
|
})
|
|
|
|
|
: Promise.resolve([]),
|
|
|
|
|
]).then(([detailRes, certList]) => {
|
|
|
|
|
if (!cancelled) {
|
|
|
|
|
setInfo(detailRes?.data || {});
|
|
|
|
|
setCertificates(Array.isArray(certList) ? certList : []);
|
|
|
|
|
}
|
|
|
|
|
}).finally(() => {
|
|
|
|
|
if (!cancelled) {
|
|
|
|
|
setDetailLoading(false);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return () => {
|
|
|
|
|
cancelled = true;
|
|
|
|
|
};
|
2026-06-29 11:59:34 +08:00
|
|
|
}, [open, currentId]);
|
2026-06-26 17:28:06 +08:00
|
|
|
|
|
|
|
|
const openCertPreview = async (record) => {
|
|
|
|
|
setCertPreview(record);
|
|
|
|
|
setCertPreviewInfo(null);
|
2026-06-29 11:59:34 +08:00
|
|
|
if (typeof requestCertDetailsRef.current !== "function") {
|
2026-06-26 17:28:06 +08:00
|
|
|
setCertPreviewInfo(record);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setCertPreviewLoading(true);
|
|
|
|
|
try {
|
2026-07-07 09:07:19 +08:00
|
|
|
const res = await requestCertDetailsRef.current({ id: record.id });
|
2026-06-26 17:28:06 +08:00
|
|
|
setCertPreviewInfo(res?.data || record);
|
2026-07-07 09:07:19 +08:00
|
|
|
} catch (err) {
|
|
|
|
|
console.warn("[StaffViewModal] load cert detail failed:", err);
|
|
|
|
|
setCertPreviewInfo(record);
|
|
|
|
|
} finally {
|
2026-06-26 17:28:06 +08:00
|
|
|
setCertPreviewLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-07 15:27:25 +08:00
|
|
|
|
|
|
|
|
const proofMaterialUrl= info.proofMaterialUrl? JSON.parse(info.proofMaterialUrl) : [];
|
|
|
|
|
|
2026-06-26 17:28:06 +08:00
|
|
|
const certPreviewFiles = certPreviewInfo?.certImgs?.length
|
|
|
|
|
? certPreviewInfo.certImgs
|
|
|
|
|
: certPreviewInfo?.certImgFiles || [];
|
2026-06-29 11:59:34 +08:00
|
|
|
const certPreviewUrl = resolvePreviewSrc(certPreviewFiles[0]);
|
2026-06-26 17:28:06 +08:00
|
|
|
const certPreviewName = certPreviewFiles[0]?.fileName || certPreviewFiles[0]?.name || certPreviewInfo?.certName;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<Modal
|
|
|
|
|
open={open}
|
2026-07-06 18:06:49 +08:00
|
|
|
destroyOnHidden
|
2026-06-26 17:28:06 +08:00
|
|
|
title="查看"
|
|
|
|
|
width={900}
|
|
|
|
|
loading={detailLoading}
|
|
|
|
|
cancelText="关闭"
|
|
|
|
|
okButtonProps={{ style: { display: "none" } }}
|
|
|
|
|
onCancel={onCancel}
|
|
|
|
|
>
|
|
|
|
|
<Descriptions bordered column={2} labelStyle={{ width: 120 }}>
|
2026-07-07 15:27:25 +08:00
|
|
|
<Descriptions.Item label="姓名">{info.userName}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="性别">{GENDER_MAP[info.genderCode]}</Descriptions.Item>
|
2026-06-26 17:28:06 +08:00
|
|
|
<Descriptions.Item label="出生日期">{info.birthDate}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="账号">{info.account}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="部门">{info.deptName}</Descriptions.Item>
|
2026-07-07 15:27:25 +08:00
|
|
|
<Descriptions.Item label="岗位">{info.postName}</Descriptions.Item>
|
2026-06-26 17:28:06 +08:00
|
|
|
<Descriptions.Item label="人员类型">{info.personType || "基础人员"}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="资质范围">{info.qualScope || "-"}</Descriptions.Item>
|
2026-07-07 15:27:25 +08:00
|
|
|
<Descriptions.Item label="职业等级">{info.professionalLevelName || "-"}</Descriptions.Item>
|
2026-06-26 17:28:06 +08:00
|
|
|
<Descriptions.Item label="证书编号">{info.evaluatorCertNo || "-"}</Descriptions.Item>
|
2026-07-07 15:27:25 +08:00
|
|
|
<Descriptions.Item label="学历类型">{info.educationTypeName || "-"}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="学历层次">{info.educationLevelName || "-"}</Descriptions.Item>
|
2026-06-26 17:28:06 +08:00
|
|
|
<Descriptions.Item label="职称">{info.titleName || "-"}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="是否注册安全工程师">
|
|
|
|
|
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="身份证号">{info.idCardNo}</Descriptions.Item>
|
2026-07-07 15:27:25 +08:00
|
|
|
|
|
|
|
|
<Descriptions.Item label="现住地址" span={2}>{info.currentAddress}</Descriptions.Item>
|
2026-06-26 17:28:06 +08:00
|
|
|
<Descriptions.Item label="办公地址" span={2}>{info.officeAddress}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="毕业院校">{info.graduateSchool}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="专业">{info.major}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="出版学术专著、专利、获奖、发表学术论文等" span={2}>
|
|
|
|
|
{info.publications || "-"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="自我申报的专业能力及认定方式" span={2}>
|
|
|
|
|
{info.abilityDeclaration || "-"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="主要学习工作经历" span={2}>
|
|
|
|
|
{info.workExperience || "-"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="申报专业能力证明材料" span={2}>
|
2026-07-07 15:27:25 +08:00
|
|
|
{proofMaterialUrl.length ? proofMaterialUrl.map((item) => (
|
|
|
|
|
<div key={item.id}>
|
|
|
|
|
<a href={item.url} target="_blank" rel="noopener noreferrer">
|
|
|
|
|
{item.fileName || item.name || item.url}
|
|
|
|
|
</a>
|
2026-07-07 09:07:19 +08:00
|
|
|
</div>
|
2026-07-07 15:27:25 +08:00
|
|
|
)) : "-"
|
|
|
|
|
}
|
2026-06-26 17:28:06 +08:00
|
|
|
</Descriptions.Item>
|
|
|
|
|
</Descriptions>
|
|
|
|
|
|
|
|
|
|
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>证照信息</div>
|
|
|
|
|
<Table
|
|
|
|
|
size="small"
|
|
|
|
|
bordered
|
|
|
|
|
rowKey="id"
|
|
|
|
|
pagination={false}
|
|
|
|
|
dataSource={certificates}
|
|
|
|
|
locale={{ emptyText: "暂无证照信息" }}
|
|
|
|
|
columns={[
|
|
|
|
|
{ title: "证照名称", dataIndex: "certName" },
|
|
|
|
|
{ title: "证书编号", dataIndex: "certNo" },
|
|
|
|
|
{ title: "证书类别", dataIndex: "certCategory" },
|
|
|
|
|
{ title: "发证机关", dataIndex: "issueOrg" },
|
|
|
|
|
{ title: "有效开始日期", dataIndex: "validStartDate", width: 120 },
|
|
|
|
|
{ title: "有效结束日期", dataIndex: "validEndDate", width: 120 },
|
|
|
|
|
{
|
|
|
|
|
title: "操作",
|
|
|
|
|
width: 90,
|
|
|
|
|
render: (_, record) => (
|
|
|
|
|
<Button type="link" size="small" onClick={() => openCertPreview(record)}>查看证书</Button>
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
|
|
<FilePreviewModal
|
|
|
|
|
open={!!attachmentPreview}
|
|
|
|
|
title="附件预览"
|
|
|
|
|
fileName={attachmentPreview?.fileName}
|
|
|
|
|
url={attachmentPreview?.url}
|
|
|
|
|
onCancel={() => setAttachmentPreview(null)}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<Modal
|
|
|
|
|
open={!!certPreview}
|
2026-07-06 18:06:49 +08:00
|
|
|
destroyOnHidden
|
2026-06-26 17:28:06 +08:00
|
|
|
title="证书预览"
|
|
|
|
|
width={720}
|
|
|
|
|
loading={certPreviewLoading}
|
|
|
|
|
cancelText="关闭"
|
|
|
|
|
okText={certPreviewUrl ? "新窗口打开" : "关闭"}
|
|
|
|
|
okButtonProps={{ style: certPreviewUrl ? undefined : { display: "none" } }}
|
|
|
|
|
onCancel={() => {
|
|
|
|
|
setCertPreview(null);
|
|
|
|
|
setCertPreviewInfo(null);
|
|
|
|
|
}}
|
|
|
|
|
onOk={() => {
|
|
|
|
|
if (certPreviewUrl) {
|
|
|
|
|
window.open(certPreviewUrl, "_blank");
|
|
|
|
|
}
|
|
|
|
|
setCertPreview(null);
|
|
|
|
|
setCertPreviewInfo(null);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{certPreviewInfo && (
|
|
|
|
|
<div>
|
|
|
|
|
<Descriptions bordered column={1} size="small" style={{ marginBottom: 12 }}>
|
|
|
|
|
<Descriptions.Item label="证照名称">{certPreviewInfo.certName}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="证书编号">{certPreviewInfo.certNo}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="证书类别">{certPreviewInfo.certCategory || "-"}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="发证机关">{certPreviewInfo.issueOrg || "-"}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="有效开始日期">{certPreviewInfo.validStartDate || "-"}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="有效结束日期">{certPreviewInfo.validEndDate || "-"}</Descriptions.Item>
|
|
|
|
|
</Descriptions>
|
|
|
|
|
<FilePreviewContent url={certPreviewUrl} fileName={certPreviewName} />
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</Modal>
|
|
|
|
|
</>
|
|
|
|
|
);
|
2026-07-07 09:07:19 +08:00
|
|
|
}
|