tangjie 2026-08-08 13:51:42 +08:00
parent 9908a0093a
commit 942fc4a55f
5 changed files with 514 additions and 477 deletions

View File

@ -13,9 +13,9 @@ module.exports = {
// API_HOST: "https://gbs-gateway.qhdsafety.com",
// API_HOST: "http://192.168.0.134",
// API_HOST: "http://192.168.0.150", //太浅
API_HOST: "http://192.168.0.150", //太浅
// API_HOST: "http://192.168.0.152",
API_HOST: "http://192.168.0.103", //huwei
//API_HOST: "http://192.168.0.103", //huwei
},
production: {
// 应用后端分支名称,部署上线需要

View File

@ -1,72 +1,59 @@
import { Button, Descriptions, Image, Modal, Table, Upload } from "antd";
import { Button, Descriptions, Image, Modal, Table } from "antd";
import { useEffect, useRef, useState } from "react";
import FilePreviewModal, {
FilePreviewContent,
} from "~/components/FilePreviewModal";
import { resolvePreviewSrc } from "~/components/CertPreviewImg";
import {
fetchStaffCertDetail,
fetchStaffCertListByPersonnelId,
} from "~/api/staffCertificate";
import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper";
import {
QUALIFICATION_INDUSTRY_OPTIONS_MAP,
REGISTER_ENGINEER_CATEGORY_MAP,
PERSONNEL_POST_TYPE_MAP,
ABILITY_SOURCE_MAP,
TITLE_LEVEL_MAP,
} from "~/enumerate/enterpriseOptions";
import {
CERT_LEVEL_LABEL_BY_TYPE,
CAPABILITY_MAP,
} from "~/enumerate/constant";
const GENDER_MAP = { 1: "男", 2: "女" };
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
const EVALUATOR_MAP = { 1: "是", 2: "否" };
const parseFileList = (raw) => {
if (!raw) return [];
if (Array.isArray(raw)) return raw;
try {
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : [];
} catch {
return [];
}
};
export default function StaffViewModal({
open,
currentId,
requestDetails = fetchOrgPersonnelDetail,
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);
const requestDetailsRef = useRef(requestDetails);
const requestCertListRef = useRef(requestCertList);
const requestCertDetailsRef = useRef(requestCertDetails);
requestDetailsRef.current = requestDetails;
requestCertListRef.current = requestCertList;
requestCertDetailsRef.current = requestCertDetails;
useEffect(() => {
if (!open || !currentId) {
setInfo({});
setCertificates([]);
return;
}
let cancelled = false;
setDetailLoading(true);
const loadCertList = requestCertListRef.current;
Promise.all([
Promise.resolve(requestDetailsRef.current({ id: currentId })).catch(
(err) => {
Promise.resolve(requestDetailsRef.current({ id: currentId }))
.catch((err) => {
console.warn("[StaffViewModal] load detail failed:", err);
return { data: {} };
},
),
typeof loadCertList === "function"
? Promise.resolve(loadCertList(currentId)).catch((err) => {
console.warn("[StaffViewModal] load certificates failed:", err);
return [];
})
: Promise.resolve([]),
])
.then(([detailRes, certList]) => {
.then((detailRes) => {
if (!cancelled) {
setInfo(detailRes?.data || {});
setCertificates(Array.isArray(certList) ? certList : []);
}
})
.finally(() => {
@ -79,51 +66,35 @@ export default function StaffViewModal({
};
}, [open, currentId]);
const openCertPreview = async (record) => {
setCertPreview(record);
setCertPreviewInfo(null);
if (typeof requestCertDetailsRef.current !== "function") {
setCertPreviewInfo(record);
return;
}
setCertPreviewLoading(true);
try {
const res = await requestCertDetailsRef.current({ id: record.id });
setCertPreviewInfo(res?.data || record);
} catch (err) {
console.warn("[StaffViewModal] load cert detail failed:", err);
setCertPreviewInfo(record);
} finally {
setCertPreviewLoading(false);
}
};
const proofMaterialFiles = parseFileList(info.proofMaterialUrl);
const proofMaterialUrl = info.proofMaterialUrl
? JSON.parse(info.proofMaterialUrl)
const titleInfo = Array.isArray(info.titleCodeArr)
? info.titleCodeArr.map((item) => ({
titleName: TITLE_LEVEL_MAP[item.titleCode] || item.titleCode || "-",
proveUrlList: parseFileList(item.proveUrl),
}))
: [];
const titleCodeArr = Array.isArray(info.titleCodeArr)
? info.titleCodeArr.map((item) => {
let proveUrlList = [];
if (item.proveUrl) {
try {
proveUrlList =
typeof item.proveUrl === "string"
? JSON.parse(item.proveUrl)
: item.proveUrl;
} catch {
proveUrlList = [];
}
}
return {
industryName:
QUALIFICATION_INDUSTRY_OPTIONS_MAP[item.industryCode] ||
item.industryCode ||
const regCert = info.registeredSafetyEngineerCert;
const regCertLevelMap = CERT_LEVEL_LABEL_BY_TYPE.registered_safety_engineer;
const regCertFiles = parseFileList(regCert?.certAttachmentUrl);
const evaluatorCert = info.evaluatorCert;
const evaluatorFlag = info.evaluatorFlag || (evaluatorCert ? 1 : 2);
const evalCertLevelMap = CERT_LEVEL_LABEL_BY_TYPE.evaluator;
const evalCertFiles = parseFileList(evaluatorCert?.certAttachmentUrl);
const capabilityList = Array.isArray(info.capabilityAssessment)
? info.capabilityAssessment.map((item) => ({
capabilityName:
CAPABILITY_MAP[item.professionalCapabilityCode] ||
item.professionalCapabilityCode ||
"-",
titleName: TITLE_LEVEL_MAP[item.titleCode] || item.titleCode || "-",
proveUrlList,
};
})
sourceName:
ABILITY_SOURCE_MAP[item.professionalCapabilitySourceCode] ||
item.professionalCapabilitySourceCode ||
"-",
}))
: [];
const renderFileList = (files) => {
@ -150,17 +121,7 @@ export default function StaffViewModal({
});
};
const certPreviewFiles = certPreviewInfo?.certImgs?.length
? certPreviewInfo.certImgs
: certPreviewInfo?.certImgFiles || [];
const certPreviewUrl = resolvePreviewSrc(certPreviewFiles[0]);
const certPreviewName =
certPreviewFiles[0]?.fileName ||
certPreviewFiles[0]?.name ||
certPreviewInfo?.certName;
return (
<>
<Modal
open={open}
destroyOnHidden
@ -188,10 +149,10 @@ export default function StaffViewModal({
<Descriptions.Item label="身份证号">
{info.idCardNo}
</Descriptions.Item>
<Descriptions.Item label="现住地址" >
<Descriptions.Item label="现住地址">
{info.currentAddress}
</Descriptions.Item>
<Descriptions.Item label="办公地址" >
<Descriptions.Item label="办公地址">
{info.officeAddress}
</Descriptions.Item>
<Descriptions.Item label="学历类型">
@ -206,67 +167,56 @@ export default function StaffViewModal({
<Descriptions.Item label="基础学科专业">
{info.basicDisciplineMajorName || "-"}
</Descriptions.Item>
<Descriptions.Item label="是否注册安全工程师">
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
</Descriptions.Item>
<Descriptions.Item label="是否技术负责人">
{info.technicalDirectorFlag === true
? "是"
: info.technicalDirectorFlag === false
? "否"
: "-"}
</Descriptions.Item>
<Descriptions.Item label="是否过程控制负责人">
{info.processControlLeaderFlag === true
? "是"
: info.processControlLeaderFlag === false
? "否"
: "-"}
</Descriptions.Item>
<Descriptions.Item
label="出版学术专著、专利、获奖、发表学术论文等"
span={2}
>
{info.publications || "-"}
</Descriptions.Item>
<Descriptions.Item label="自我申报的专业能力及认定方式" span={2}>
{info.abilityDeclaration || "-"}
<Descriptions.Item label="人员岗位类型">
{PERSONNEL_POST_TYPE_MAP[info.personnelPositionType] || "-"}
</Descriptions.Item>
<Descriptions.Item label="主要学习工作经历" span={2}>
{info.workExperience || "-"}
</Descriptions.Item>
<Descriptions.Item label="学历证明材料" span={2}>
{proofMaterialUrl.length
? proofMaterialUrl.map((item) => {
const isImage =
/\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?.*)?$/i.test(item.url);
return (
<div key={item.id}>
{isImage ? (
<Image
src={item.url}
style={{ maxWidth: 200, cursor: "pointer" }}
preview={{
mask: item.fileName || item.name || "预览",
}}
/>
) : (
<Button
type="link"
onClick={() => window.open(item.url)}
>
{item.fileName || item.name || item.url}
</Button>
)}
</div>
);
})
: "-"}
{renderFileList(proofMaterialFiles)}
</Descriptions.Item>
</Descriptions>
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
注安信息
</div>
<Descriptions bordered column={2} labelStyle={{ width: 120 }}>
<Descriptions.Item label="是否注册安全工程师">
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
</Descriptions.Item>
<Descriptions.Item label="注安专业类别">
{REGISTER_ENGINEER_CATEGORY_MAP[regCert?.certCategoryCode] || "-"}
</Descriptions.Item>
<Descriptions.Item label="注安等级">
{regCertLevelMap[regCert?.certLevel] || "-"}
</Descriptions.Item>
<Descriptions.Item label="证书编号">
{regCert?.certNo || "-"}
</Descriptions.Item>
<Descriptions.Item label="注安证书" span={2}>
{renderFileList(regCertFiles)}
</Descriptions.Item>
</Descriptions>
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
评价师信息
</div>
<Descriptions bordered column={2} labelStyle={{ width: 120 }}>
<Descriptions.Item label="是否评价师">
{EVALUATOR_MAP[evaluatorFlag] ?? "-"}
</Descriptions.Item>
<Descriptions.Item label="评价师等级">
{evalCertLevelMap[evaluatorCert?.certLevel] || "-"}
</Descriptions.Item>
<Descriptions.Item label="证书编号">
{evaluatorCert?.certNo || "-"}
</Descriptions.Item>
<Descriptions.Item label="评价师证书" span={2}>
{renderFileList(evalCertFiles)}
</Descriptions.Item>
</Descriptions>
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
职称信息
</div>
@ -275,10 +225,9 @@ export default function StaffViewModal({
bordered
pagination={false}
rowKey={(_, idx) => idx}
dataSource={titleCodeArr}
dataSource={titleInfo}
columns={[
{ title: "行业", dataIndex: "industryName", width: 150 },
{ title: "职称", dataIndex: "titleName", width: 100 },
{ title: "职称", dataIndex: "titleName", width: 120 },
{
title: "证明材料",
dataIndex: "proveUrlList",
@ -288,120 +237,20 @@ export default function StaffViewModal({
/>
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
证照信息
专业能力信息
</div>
<Table
size="small"
bordered
rowKey="id"
pagination={false}
dataSource={certificates}
locale={{ emptyText: "暂无证照信息" }}
rowKey={(_, idx) => idx}
dataSource={capabilityList}
locale={{ emptyText: "暂无专业能力信息" }}
columns={[
{ title: "证照名称", dataIndex: "certName", ellipsis: true },
{ title: "证书编号", dataIndex: "certNo", ellipsis: true },
{ title: "证书类别", dataIndex: "certTypeName", ellipsis: true },
{ title: "发证机关", dataIndex: "issueOrg", ellipsis: true },
{ title: "有效开始日期", dataIndex: "validStartDate", width: 120 },
{ title: "有效结束日期", dataIndex: "validEndDate", width: 120 },
{
title: "操作",
width: 90,
render: (_, record) => (
<Button
type="link"
size="small"
onClick={() => openCertPreview(record)}
>
查看证书
</Button>
),
},
{ title: "专业能力", dataIndex: "capabilityName" },
{ title: "来源", dataIndex: "sourceName", width: 280 },
]}
/>
</Modal>
<FilePreviewModal
open={!!attachmentPreview}
title="附件预览"
fileName={attachmentPreview?.fileName}
url={attachmentPreview?.url}
onCancel={() => setAttachmentPreview(null)}
/>
<Modal
open={!!certPreview}
destroyOnHidden
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.certTypeName || "-"}
</Descriptions.Item>
<Descriptions.Item label="发证机关">
{certPreviewInfo.issueOrg || "-"}
</Descriptions.Item>
<Descriptions.Item label="有效开始日期">
{certPreviewInfo.validStartDate || "-"}
</Descriptions.Item>
<Descriptions.Item label="有效结束日期">
{certPreviewInfo.validEndDate || "-"}
</Descriptions.Item>
<Descriptions.Item label="查看证书">
{certPreviewInfo.certAttachmentUrl &&
certPreviewInfo.certAttachmentUrl.split(",").map((item) => {
const isImage =
/\.(png|jpe?g|gif|bmp|webp|svg)(\?|#|$)/i.test(item);
return (
<div key={item} style={{ marginBottom: 4 }}>
{isImage ? (
<Image
src={item}
width={100}
style={{ cursor: "pointer" }}
/>
) : (
<a onClick={() => window.open(item)}>{item}</a>
)}
</div>
);
})}
</Descriptions.Item>
</Descriptions>
</div>
)}
</Modal>
</>
);
}

View File

@ -162,6 +162,50 @@ export const REGISTER_ENGINEER_OPTIONS = [
{ label: "否", value: 2 },
];
/** 注安专业类别 */
export const REGISTER_ENGINEER_CATEGORY_OPTIONS = [
{ label: "煤矿安全", value: "COAL_MINE" },
{ label: "金属非金属矿山安全", value: "METAL_NONMETAL_MINE" },
{ label: "化工安全", value: "CHEMICAL" },
{ label: "金属冶炼安全", value: "METAL_SMELTING" },
{ label: "建筑施工安全", value: "CONSTRUCTION" },
{ label: "道路运输安全", value: "ROAD_TRANSPORT" },
{ label: "其他安全", value: "OTHER" },
];
export const REGISTER_ENGINEER_CATEGORY_MAP =
REGISTER_ENGINEER_CATEGORY_OPTIONS.reduce(
(acc, cur) => ({ ...acc, [cur.value]: cur.label }),
{},
);
/** 人员岗位类型 */
export const PERSONNEL_POST_TYPE_OPTIONS = [
{ label: "技术负责人", value: "TECHNICAL_LEADER" },
{ label: "过程控制负责人", value: "PROCESS_CONTROL_LEADER" },
];
export const PERSONNEL_POST_TYPE_MAP = PERSONNEL_POST_TYPE_OPTIONS.reduce(
(acc, cur) => ({ ...acc, [cur.value]: cur.label }),
{},
);
/** 能力来源 */
export const ABILITY_SOURCE_OPTIONS = [
{ label: "注册安全工程师", value: "REGISTERED_SAFETY_ENGINEER" },
{ label: "学历", value: "EDUCATION" },
{ label: "职称", value: "PROFESSIONAL_TITLE" },
{ label: "出版学术专著、专利、获奖、发表学术论文等", value: "ACADEMIC_ACHIEVEMENT" },
];
export const ABILITY_SOURCE_MAP = ABILITY_SOURCE_OPTIONS.reduce(
(acc, cur) => ({ ...acc, [cur.value]: cur.label }),
{},
);
/** 是否评价师 */
export const EVALUATOR_OPTIONS = [
{ label: "是", value: 1 },
{ label: "否", value: 2 },
];
/** 职称等级 */
export const TITLE_LEVEL_OPTIONS = [
{ value: "SENIOR", label: "高级" },

View File

@ -14,6 +14,7 @@ import {
Table,
TreeSelect,
Upload,
Divider,
} from "antd";
import { InboxOutlined } from "@ant-design/icons";
import { Get } from "@cqsjjb/jjb-common-lib/http";
@ -29,14 +30,19 @@ import { NS_STAFF_INFO, NS_ORG_DEPARTMENT } from "~/enumerate/namespace";
import {
EDUCATION_LEVEL_OPTIONS,
EDUCATION_TYPE_OPTIONS,
QUALIFICATION_INDUSTRY_OPTIONS,
REGISTER_ENGINEER_OPTIONS,
REGISTER_ENGINEER_CATEGORY_OPTIONS,
PERSONNEL_POST_TYPE_OPTIONS,
ABILITY_SOURCE_OPTIONS,
EVALUATOR_OPTIONS,
TITLE_LEVEL_OPTIONS,
} from "~/enumerate/enterpriseOptions";
import { CERT_LEVEL_OPTIONS_BY_TYPE, CAPABILITY_OPTIONS } from "~/enumerate/constant";
import { getBirthDateFromIdCard } from "~/utils";
import { idCardRule, mobileRule } from "~/utils/validators";
import AttachmentUpload from "~/components/AttachmentUpload";
import StaffViewModal from "~/components/StaffViewModal";
import "./index.less";
const { router } = tools;
const API_HOST = window.process?.env?.app?.API_HOST || "";
@ -218,7 +224,7 @@ function PersonnelInfoPage(props) {
width: 260,
dataIndex: "abilityNames",
ellipsis: true,
render: (text) => text || '暂无',
render: (text) => text || "暂无",
},
{
title: "操作",
@ -245,16 +251,7 @@ function PersonnelInfoPage(props) {
>
编辑
</Button>
<Button
type="link"
size="small"
onClick={() => {
setCurrentId(record.id);
goCertificate(record.id, record.userName);
}}
>
证书能力
</Button>
<Button
type="link"
size="small"
@ -361,6 +358,11 @@ function StaffFormModal({
const [deptPositionOptions, setDeptPositionOptions] = useState([]);
const [positionLoading, setPositionLoading] = useState(false);
const watchedDeptId = Form.useWatch("deptId", form);
const watchedRegisterEngineerFlag = Form.useWatch(
"registerEngineerFlag",
form,
);
const watchedEvaluatorFlag = Form.useWatch("evaluatorFlag", form);
const wasOpenRef = useRef(false);
const inflightDeptRef = useRef(null);
@ -396,7 +398,6 @@ function StaffFormModal({
};
const loadBasicDisciplineMajors = async () => {
await queryBasicDisciplineMajor({ pageSize: 999 });
};
@ -438,21 +439,37 @@ function StaffFormModal({
setFields.proofMaterialUrl = JSON.parse(setFields.proofMaterialUrl);
}
if (Array.isArray(setFields.titleCodeArr)) {
setFields.titleCodeArr = setFields.titleCodeArr.map((item) => {
if (item.proveUrl && typeof item.proveUrl === "string") {
const parseCertAttachment = (cert) => {
if (cert?.certAttachmentUrl) {
try {
return { ...item, proveUrl: JSON.parse(item.proveUrl) };
cert.certAttachmentUrl = JSON.parse(cert.certAttachmentUrl);
} catch {}
}
};
if (setFields.registeredSafetyEngineerCert) {
parseCertAttachment(setFields.registeredSafetyEngineerCert);
}
if (setFields.evaluatorCert) {
setFields.evaluatorFlag = 1;
parseCertAttachment(setFields.evaluatorCert);
}
if (Array.isArray(setFields.titleCodeArr) && setFields.titleCodeArr[0]) {
const first = setFields.titleCodeArr[0];
setFields.titleCode = first.titleCode;
if (first.proveUrl) {
try {
setFields.proveUrl = JSON.parse(first.proveUrl);
} catch {
return item;
setFields.proveUrl = first.proveUrl;
}
}
return item;
});
delete setFields.titleCodeArr;
}
form.setFieldsValue(setFields);
})
.finally(() => {
if (!cancelled) setDetailLoading(false);
@ -488,6 +505,15 @@ function StaffFormModal({
const handleSubmit = async (values) => {
try {
setSubmitting(true);
const stringifyCert = (cert) =>
cert
? {
...cert,
certAttachmentUrl: Array.isArray(cert.certAttachmentUrl)
? JSON.stringify(cert.certAttachmentUrl)
: undefined,
}
: undefined;
const payload = {
...values,
genderName:
@ -499,13 +525,28 @@ function StaffFormModal({
proofMaterialUrl: Array.isArray(values.proofMaterialUrl)
? JSON.stringify(values.proofMaterialUrl)
: undefined,
titleCodeArr: Array.isArray(values.titleCodeArr)
? values.titleCodeArr.map((item) => ({
...item,
proveUrl: Array.isArray(item.proveUrl)
? JSON.stringify(item.proveUrl)
: item.proveUrl,
}))
titleCodeArr: [
{
titleCode: values.titleCode,
proveUrl: Array.isArray(values.proveUrl)
? JSON.stringify(values.proveUrl)
: undefined,
},
],
registeredSafetyEngineerCert:
values.registerEngineerFlag === 1
? stringifyCert(values.registeredSafetyEngineerCert)
: undefined,
evaluatorCert:
values.evaluatorFlag === 1
? stringifyCert(values.evaluatorCert)
: undefined,
capabilityAssessment: Array.isArray(values.capabilityAssessment)
? values.capabilityAssessment.filter(
(item) =>
item?.professionalCapabilityCode ||
item?.professionalCapabilitySourceCode,
)
: undefined,
};
if (payload.birthDate && typeof payload.birthDate !== "string") {
@ -515,7 +556,6 @@ function StaffFormModal({
payload.joinWorkDate = payload.joinWorkDate.format("YYYY-MM-DD");
}
if (currentId) payload.id = currentId;
delete payload.proofMaterials;
const request = currentId ? staffInfoEdit : staffInfoAdd;
const res = await request(payload);
@ -540,14 +580,6 @@ function StaffFormModal({
confirmLoading={submitting}
onCancel={handleCancel}
onOk={() => {
const titleArr = form.getFieldValue("titleCodeArr") || [];
const hasTitleInfo = titleArr.some(
(item) => item?.industryCode && item?.titleCode,
);
if (!hasTitleInfo) {
message.warning("请添加职称信息");
return;
}
form.submit();
}}
>
@ -586,7 +618,11 @@ function StaffFormModal({
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="birthDate" label="出生日期" rules={[{ required: true, message: "请选择出生日期" }]}>
<Form.Item
name="birthDate"
label="出生日期"
rules={[{ required: true, message: "请选择出生日期" }]}
>
<DatePicker style={{ width: "100%" }} />
</Form.Item>
</Col>
@ -745,7 +781,21 @@ function StaffFormModal({
maxCount={5}
/>
</Col>
<Col span={12}>
<Form.Item name="personnelPositionType" label="人员岗位类型">
<Select
placeholder="请选择人员岗位类型"
allowClear
options={PERSONNEL_POST_TYPE_OPTIONS}
/>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={24}>
<div className="register-engineer-section">
<div className="register-engineer-section-title">注安信息</div>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="registerEngineerFlag"
@ -758,39 +808,163 @@ function StaffFormModal({
/>
</Form.Item>
</Col>
{watchedRegisterEngineerFlag === 1 && (
<>
<Col span={12}>
<Form.Item name="technicalDirectorFlag" label="是否技术负责人">
<Form.Item
name={["registeredSafetyEngineerCert", "certCategoryCode"]}
label="注安专业类别"
rules={[{ required: true, message: "请选择注安专业类别" }]}
>
<Select
placeholder="请选择"
placeholder="请选择注安专业类别"
allowClear
options={[
{ label: "是", value: true },
{ label: "否", value: false },
]}
options={REGISTER_ENGINEER_CATEGORY_OPTIONS}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="processControlLeaderFlag"
label="是否过程控制负责人"
name={["registeredSafetyEngineerCert", "certLevel"]}
label="注安等级"
rules={[{ required: true, message: "请选择注安等级" }]}
>
<Select
placeholder="请选择"
placeholder="请选择注安等级"
allowClear
options={[
{ label: "是", value: true },
{ label: "否", value: false },
]}
options={TITLE_LEVEL_OPTIONS}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name={["registeredSafetyEngineerCert", "certNo"]}
label="证书编号"
rules={[
{ required: true, message: "请输入证书编号" },
{ max: 50, message: "证书编号不能超过50个字符" },
]}
>
<Input placeholder="请输入证书编号" maxLength={50} />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item label="职称信息" required>
<AttachmentUpload
name={["registeredSafetyEngineerCert", "certAttachmentUrl"]}
label="注安证书"
extra="最多上传10个pdf,doc,docx,图片格式文件"
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"
maxCount={10}
rules={[{ required: true, message: "请上传注安证书" }]}
/>
</Col>
</>
)}
</Row>
</div>
</Col>
</Row>
<Row gutter={16}>
<Col span={24}>
<div className="register-engineer-section">
<div className="register-engineer-section-title">
评价师信息
</div>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="evaluatorFlag"
label="是否评价师"
initialValue={2}
>
<Select
placeholder="请选择"
options={EVALUATOR_OPTIONS}
/>
</Form.Item>
</Col>
{watchedEvaluatorFlag === 1 && (
<>
<Col span={12}>
<Form.Item
name={["evaluatorCert", "certLevel"]}
label="评价师等级"
rules={[{ required: true, message: "请选择评价师等级" }]}
>
<Select
placeholder="请选择评价师等级"
allowClear
options={CERT_LEVEL_OPTIONS_BY_TYPE.evaluator}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name={["evaluatorCert", "certNo"]}
label="证书编号"
rules={[
{ required: true, message: "请输入证书编号" },
{ max: 50, message: "证书编号不能超过50个字符" },
]}
>
<Input placeholder="请输入证书编号" maxLength={50} />
</Form.Item>
</Col>
<Col span={24}>
<AttachmentUpload
name={["evaluatorCert", "certAttachmentUrl"]}
label="评价师证书"
extra="最多上传10个pdf,doc,docx,图片格式文件"
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"
maxCount={10}
rules={[{ required: true, message: "请上传评价师证书" }]}
/>
</Col>
</>
)}
</Row>
</div>
</Col>
</Row>
<Row gutter={16}>
<Col span={24}>
<div className="register-engineer-section">
<div className="register-engineer-section-title">
职称信息
</div>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="titleCode"
label="职称"
rules={[{ required: true, message: "请选择职称" }]}
>
<Select
placeholder="请选择职称"
allowClear
options={TITLE_LEVEL_OPTIONS}
/>
</Form.Item>
</Col>
<Col span={12}>
<AttachmentUpload
name="proveUrl"
label="证明材料"
extra="最多上传1个pdf,doc,docx,图片格式文件"
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"
maxCount={1}
rules={[{ required: true, message: "请上传证明材料" }]}
/>
</Col>
</Row>
</div>
</Col>
<Col span={24}>
<Form.Item label="专业能力信息">
<Form.List
name="titleCodeArr"
name="capabilityAssessment"
initialValue={[{}]}
rules={[{ required: true, message: "请添加职称信息" }]}
>
{(fields, { add, remove }) => (
<>
@ -800,60 +974,38 @@ function StaffFormModal({
pagination={false}
size="small"
bordered
locale={{ emptyText: "暂无职称信息,点击下方按钮添加" }}
columns={[
{
title: "行业",
width: 200,
title: "专业能力",
render: (_, field) => (
<Form.Item
name={[field.name, "industryCode"]}
name={[field.name, "professionalCapabilityCode"]}
style={{ marginBottom: 0 }}
rules={[
{ required: true, message: "请选择行业" },
]}
>
<Select
placeholder="请选择行业"
placeholder="请选择专业能力"
allowClear
options={QUALIFICATION_INDUSTRY_OPTIONS}
options={CAPABILITY_OPTIONS}
/>
</Form.Item>
),
},
{
title: "职称",
width: 120,
title: "来源",
width: 280,
render: (_, field) => (
<Form.Item
name={[field.name, "titleCode"]}
name={[field.name, "professionalCapabilitySourceCode"]}
style={{ marginBottom: 0 }}
rules={[
{ required: true, message: "请选择职称" },
]}
>
<Select
placeholder="请选择职称"
placeholder="请选择来源"
allowClear
options={TITLE_LEVEL_OPTIONS}
options={ABILITY_SOURCE_OPTIONS}
/>
</Form.Item>
),
},
{
title: "证明材料",
render: (_, field) => (
<AttachmentUpload
name={[field.name, "proveUrl"]}
rules={[
{ required: true, message: "请上传证明材料" },
]}
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"
maxCount={1}
style={{ marginBottom: 0 }}
/>
),
},
{
title: "操作",
width: 80,
@ -871,47 +1023,21 @@ function StaffFormModal({
},
]}
/>
{fields.length < 2 && (
<Button
type="dashed"
onClick={() => add()}
block
style={{ marginTop: 12 }}
>
添加职称信息
添加专业能力信息
</Button>
)}
</>
)}
</Form.List>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
name="publications"
label="出版学术专著、专利、获奖、发表学术论文等"
rules={[{ max: 500, message: "不能超过1000个字符" }]}
>
<Input.TextArea
rows={2}
placeholder="请输入"
maxLength={500}
showCount
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
name="abilityDeclaration"
label="自我申报的专业能力及认定方式"
rules={[{ max: 500, message: "不能超过1000个字符" }]}
>
<Input.TextArea
rows={2}
placeholder="请输入"
maxLength={500}
showCount
/>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item name="workExperience" label="主要学习工作经历">
<Input.TextArea
@ -1073,7 +1199,12 @@ function StaffImportModal({ open, onCancel, onSuccess }) {
);
}
function ResetPasswordModal({ open, currentId, staffInfoResetPassword, onCancel }) {
function ResetPasswordModal({
open,
currentId,
staffInfoResetPassword,
onCancel,
}) {
const [form] = Form.useForm();
const [submitting, setSubmitting] = useState(false);

View File

@ -0,0 +1,13 @@
.register-engineer-section {
border: 1px dashed #d9d9d9;
border-radius: 8px;
padding: 16px 16px 0;
margin-bottom: 24px;
&-title {
font-size: 14px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
margin-bottom: 16px;
}
}