commit
24a5336d5d
|
|
@ -10,12 +10,12 @@ module.exports = {
|
|||
javaGitBranch: "dev",
|
||||
// 本地联调 safetyEval-service(context-path: /safetyEval,默认端口 8095)
|
||||
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
|
||||
// API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||
// API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||
|
||||
//API_HOST: "http://192.168.0.152",
|
||||
//API_HOST: "http://192.168.0.150", //太浅
|
||||
// API_HOST: "http://192.168.0.134",
|
||||
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: {
|
||||
// 应用后端分支名称,部署上线需要
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ import {
|
|||
fetchStaffCertListByPersonnelId,
|
||||
} from "~/api/staffCertificate";
|
||||
import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper";
|
||||
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP, TITLE_LEVEL_MAP } from "~/enumerate/enterpriseOptions";
|
||||
import {
|
||||
QUALIFICATION_INDUSTRY_OPTIONS_MAP,
|
||||
TITLE_LEVEL_MAP,
|
||||
} from "~/enumerate/enterpriseOptions";
|
||||
|
||||
const GENDER_MAP = { 1: "男", 2: "女" };
|
||||
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
|
||||
|
|
@ -99,6 +102,54 @@ export default function StaffViewModal({
|
|||
? JSON.parse(info.proofMaterialUrl)
|
||||
: [];
|
||||
|
||||
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 ||
|
||||
"-",
|
||||
titleName: TITLE_LEVEL_MAP[item.titleCode] || item.titleCode || "-",
|
||||
proveUrlList,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
const renderFileList = (files) => {
|
||||
if (!files?.length) return "-";
|
||||
return files.map((item) => {
|
||||
const isImage = /\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?.*)?$/i.test(
|
||||
item.url,
|
||||
);
|
||||
return (
|
||||
<div key={item.uid || item.url}>
|
||||
{isImage ? (
|
||||
<Image
|
||||
src={item.url}
|
||||
style={{ maxWidth: 150, cursor: "pointer" }}
|
||||
preview={{ mask: item.fileName || item.name || "预览" }}
|
||||
/>
|
||||
) : (
|
||||
<Button type="link" onClick={() => window.open(item.url)}>
|
||||
{item.fileName || item.name || item.url}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const certPreviewFiles = certPreviewInfo?.certImgs?.length
|
||||
? certPreviewInfo.certImgs
|
||||
: certPreviewInfo?.certImgFiles || [];
|
||||
|
|
@ -134,35 +185,30 @@ export default function StaffViewModal({
|
|||
<Descriptions.Item label="账号">{info.account}</Descriptions.Item>
|
||||
<Descriptions.Item label="部门">{info.deptName}</Descriptions.Item>
|
||||
<Descriptions.Item label="岗位">{info.postName}</Descriptions.Item>
|
||||
<Descriptions.Item label="人员类型">
|
||||
{info.personTypeName || "基础人员"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="资质范围">
|
||||
{QUALIFICATION_INDUSTRY_OPTIONS_MAP[info.qualScope] || info.qualScope || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="职业等级">
|
||||
{info.professionalLevelName || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="证书编号">
|
||||
{info.evaluatorCertNo || "-"}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="学历类型">
|
||||
{info.educationTypeName || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="学历层次">
|
||||
{info.educationLevelName || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="职称">
|
||||
{TITLE_LEVEL_MAP[info.titleCode] ?? (info.titleCode || "-")}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="是否注册安全工程师">
|
||||
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="是否技术负责人">
|
||||
{info.technicalDirectorFlag === true ? "是" : info.technicalDirectorFlag === false ? "否" : "-"}
|
||||
{info.technicalDirectorFlag === true
|
||||
? "是"
|
||||
: info.technicalDirectorFlag === false
|
||||
? "否"
|
||||
: "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="是否过程控制负责人">
|
||||
{info.processControlLeaderFlag === true ? "是" : info.processControlLeaderFlag === false ? "否" : "-"}
|
||||
{info.processControlLeaderFlag === true
|
||||
? "是"
|
||||
: info.processControlLeaderFlag === false
|
||||
? "否"
|
||||
: "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="身份证号">
|
||||
{info.idCardNo}
|
||||
|
|
@ -177,7 +223,7 @@ export default function StaffViewModal({
|
|||
<Descriptions.Item label="毕业院校">
|
||||
{info.graduateSchool}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="专业">{info.major}</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item
|
||||
label="出版学术专著、专利、获奖、发表学术论文等"
|
||||
span={2}
|
||||
|
|
@ -190,7 +236,7 @@ export default function StaffViewModal({
|
|||
<Descriptions.Item label="主要学习工作经历" span={2}>
|
||||
{info.workExperience || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="申报专业能力证明材料" span={2}>
|
||||
<Descriptions.Item label="学历证明材料" span={2}>
|
||||
{proofMaterialUrl.length
|
||||
? proofMaterialUrl.map((item) => {
|
||||
const isImage =
|
||||
|
|
@ -219,6 +265,25 @@ export default function StaffViewModal({
|
|||
: "-"}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
||||
职称信息
|
||||
</div>
|
||||
<Table
|
||||
size="small"
|
||||
bordered
|
||||
pagination={false}
|
||||
rowKey={(_, idx) => idx}
|
||||
dataSource={titleCodeArr}
|
||||
columns={[
|
||||
{ title: "行业", dataIndex: "industryName", width: 150 },
|
||||
{ title: "职称", dataIndex: "titleName", width: 100 },
|
||||
{
|
||||
title: "证明材料",
|
||||
dataIndex: "proveUrlList",
|
||||
render: renderFileList,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
||||
证照信息
|
||||
|
|
@ -231,10 +296,10 @@ export default function StaffViewModal({
|
|||
dataSource={certificates}
|
||||
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: "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 },
|
||||
{
|
||||
|
|
@ -314,11 +379,16 @@ export default function StaffViewModal({
|
|||
<Descriptions.Item label="查看证书">
|
||||
{certPreviewInfo.certAttachmentUrl &&
|
||||
certPreviewInfo.certAttachmentUrl.split(",").map((item) => {
|
||||
const isImage = /\.(png|jpe?g|gif|bmp|webp|svg)(\?|#|$)/i.test(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" }} />
|
||||
<Image
|
||||
src={item}
|
||||
width={100}
|
||||
style={{ cursor: "pointer" }}
|
||||
/>
|
||||
) : (
|
||||
<a onClick={() => window.open(item)}>{item}</a>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -481,3 +481,5 @@ export const NATIONAL_COUNTIES = (() => {
|
|||
});
|
||||
return result;
|
||||
})();
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -138,27 +138,7 @@ export const PERSON_TYPE_OPTIONS = [
|
|||
{ label: "专职评价师", value: "专职评价师" },
|
||||
];
|
||||
|
||||
/** 职业等级 */
|
||||
export const PROFESSIONAL_LEVEL_OPTIONS = [
|
||||
{
|
||||
label: "三级安全评价师(对应职业技能等级三级 / 高级工,入门级)",
|
||||
value: "三级安全评价师(对应职业技能等级三级 / 高级工,入门级)",
|
||||
},
|
||||
{
|
||||
label: "二级安全评价师(对应职业技能等级二级 / 技师,中级)",
|
||||
value: "二级安全评价师(对应职业技能等级二级 / 技师,中级)",
|
||||
},
|
||||
{
|
||||
label: "一级安全评价师(对应职业技能等级一级 / 高级技师,最高级)",
|
||||
value: "一级安全评价师(对应职业技能等级一级 / 高级技师,最高级)",
|
||||
},
|
||||
];
|
||||
|
||||
export const PROFESSIONAL_LEVEL_OPTIONS_MAP =
|
||||
PROFESSIONAL_LEVEL_OPTIONS.reduce(
|
||||
(acc, cur) => ({ ...acc, [cur.value]: cur.label }),
|
||||
{},
|
||||
);
|
||||
|
||||
/** 学历类型 */
|
||||
export const EDUCATION_TYPE_OPTIONS = [
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
DatePicker,
|
||||
Form,
|
||||
|
|
@ -9,9 +10,12 @@ import {
|
|||
Row,
|
||||
Col,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
} from "antd";
|
||||
import { InboxOutlined } from "@ant-design/icons";
|
||||
import { Get } from "@cqsjjb/jjb-common-lib/http";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import dayjs from "dayjs";
|
||||
|
|
@ -25,8 +29,6 @@ import { NS_STAFF_INFO, NS_ORG_DEPARTMENT } from "~/enumerate/namespace";
|
|||
import {
|
||||
EDUCATION_LEVEL_OPTIONS,
|
||||
EDUCATION_TYPE_OPTIONS,
|
||||
PERSON_TYPE_OPTIONS,
|
||||
PROFESSIONAL_LEVEL_OPTIONS,
|
||||
QUALIFICATION_INDUSTRY_OPTIONS,
|
||||
REGISTER_ENGINEER_OPTIONS,
|
||||
TITLE_LEVEL_OPTIONS,
|
||||
|
|
@ -37,10 +39,12 @@ import AttachmentUpload from "~/components/AttachmentUpload";
|
|||
import StaffViewModal from "~/components/StaffViewModal";
|
||||
|
||||
const { router } = tools;
|
||||
const API_HOST = window.process?.env?.app?.API_HOST || "";
|
||||
|
||||
function PersonnelInfoPage(props) {
|
||||
const [formModalOpen, setFormModalOpen] = useState(false);
|
||||
const [viewModalOpen, setViewModalOpen] = useState(false);
|
||||
const [importModalOpen, setImportModalOpen] = useState(false);
|
||||
const [currentId, setCurrentId] = useState("");
|
||||
const [searchForm] = Form.useForm();
|
||||
const [deptOptions, setDeptOptions] = useState([]);
|
||||
|
|
@ -136,15 +140,18 @@ function PersonnelInfoPage(props) {
|
|||
</div>
|
||||
}
|
||||
extra={
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setCurrentId("");
|
||||
setFormModalOpen(true);
|
||||
}}
|
||||
>
|
||||
新增人员
|
||||
</Button>
|
||||
<Space>
|
||||
<Button onClick={() => setImportModalOpen(true)}>导入人员</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
setCurrentId("");
|
||||
setFormModalOpen(true);
|
||||
}}
|
||||
>
|
||||
新增人员
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<SearchForm
|
||||
|
|
@ -214,6 +221,13 @@ function PersonnelInfoPage(props) {
|
|||
dataIndex: "certNames",
|
||||
ellipsis: true,
|
||||
render: (text) => text || "-",
|
||||
},
|
||||
{
|
||||
title: "能力",
|
||||
width: 80,
|
||||
dataIndex: "abilityNames",
|
||||
ellipsis: true,
|
||||
render: (text) => '暂无',
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
|
|
@ -248,7 +262,7 @@ function PersonnelInfoPage(props) {
|
|||
goCertificate(record.id, record.userName);
|
||||
}}
|
||||
>
|
||||
证书
|
||||
证书能力
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
|
|
@ -315,6 +329,13 @@ function PersonnelInfoPage(props) {
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
{importModalOpen && (
|
||||
<StaffImportModal
|
||||
open={importModalOpen}
|
||||
onCancel={() => setImportModalOpen(false)}
|
||||
onSuccess={handleSearch}
|
||||
/>
|
||||
)}
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
|
|
@ -372,13 +393,14 @@ function StaffFormModal({
|
|||
}
|
||||
};
|
||||
|
||||
const loadBasicDisciplineMajors = async (industryCode) => {
|
||||
if (!industryCode) return;
|
||||
await queryBasicDisciplineMajor({ industryCode, pageSize: 999 });
|
||||
const loadBasicDisciplineMajors = async () => {
|
||||
|
||||
await queryBasicDisciplineMajor({ pageSize: 999 });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open && !wasOpenRef.current) {
|
||||
loadBasicDisciplineMajors();
|
||||
if (!currentId) {
|
||||
form.resetFields();
|
||||
setDeptPositionOptions([]);
|
||||
|
|
@ -407,14 +429,28 @@ function StaffFormModal({
|
|||
...data,
|
||||
};
|
||||
if (data.birthDate) setFields.birthDate = dayjs(data.birthDate);
|
||||
if (data.joinWorkDate) setFields.joinWorkDate = dayjs(data.joinWorkDate);
|
||||
if (data.joinWorkDate)
|
||||
setFields.joinWorkDate = dayjs(data.joinWorkDate);
|
||||
|
||||
if (setFields.proofMaterialUrl) {
|
||||
setFields.proofMaterialUrl = JSON.parse(setFields.proofMaterialUrl);
|
||||
}
|
||||
|
||||
if (Array.isArray(setFields.titleCodeArr)) {
|
||||
setFields.titleCodeArr = setFields.titleCodeArr.map((item) => {
|
||||
if (item.proveUrl && typeof item.proveUrl === "string") {
|
||||
try {
|
||||
return { ...item, proveUrl: JSON.parse(item.proveUrl) };
|
||||
} catch {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
form.setFieldsValue(setFields);
|
||||
loadBasicDisciplineMajors(data.qualScope);
|
||||
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setDetailLoading(false);
|
||||
|
|
@ -443,7 +479,7 @@ function StaffFormModal({
|
|||
}
|
||||
if ("qualScope" in changed) {
|
||||
form.setFieldValue("basicDisciplineMajorId", undefined);
|
||||
loadBasicDisciplineMajors(changed.qualScope);
|
||||
loadBasicDisciplineMajors();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -461,6 +497,14 @@ 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,
|
||||
}))
|
||||
: undefined,
|
||||
};
|
||||
if (payload.birthDate && typeof payload.birthDate !== "string") {
|
||||
payload.birthDate = payload.birthDate.format("YYYY-MM-DD");
|
||||
|
|
@ -478,7 +522,6 @@ function StaffFormModal({
|
|||
handleCancel();
|
||||
onSuccess();
|
||||
} else {
|
||||
|
||||
}
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
|
|
@ -536,7 +579,11 @@ function StaffFormModal({
|
|||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="joinWorkDate" label="参加工作时间" rules={[{ required: true, message: "请选择参加工作时间" }]}>
|
||||
<Form.Item
|
||||
name="joinWorkDate"
|
||||
label="参加工作时间"
|
||||
rules={[{ required: true, message: "请选择参加工作时间" }]}
|
||||
>
|
||||
<DatePicker style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
|
@ -544,12 +591,9 @@ function StaffFormModal({
|
|||
<Form.Item
|
||||
name="account"
|
||||
label="账号"
|
||||
rules={[
|
||||
mobileRule("账号", true),
|
||||
|
||||
]}
|
||||
rules={[mobileRule("账号", true)]}
|
||||
>
|
||||
<Input placeholder="请输入账号" />
|
||||
<Input placeholder="请输入账号" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -601,27 +645,7 @@ function StaffFormModal({
|
|||
<Input placeholder="请输入身份证号" maxLength={18} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="personTypeCode" label="人员类型">
|
||||
<Select
|
||||
placeholder="请选择人员类型"
|
||||
options={PERSON_TYPE_OPTIONS}
|
||||
onChange={(_val, option) => {
|
||||
form.setFieldValue("personTypeName", option?.label || undefined);
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="personTypeName" noStyle />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="qualScope" label="资质范围">
|
||||
<Select
|
||||
placeholder="请选择资质范围"
|
||||
allowClear
|
||||
options={QUALIFICATION_INDUSTRY_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Form.Item name="basicDisciplineMajorId" label="基础学科专业">
|
||||
<Select
|
||||
|
|
@ -629,37 +653,18 @@ function StaffFormModal({
|
|||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
options={(staffInfo?.basicDisciplineMajorList || []).map((item) => ({
|
||||
label: item.professionalName,
|
||||
value: item.id,
|
||||
}))}
|
||||
options={(staffInfo?.basicDisciplineMajorList || []).map(
|
||||
(item) => ({
|
||||
label: `${item.professionalName}`,
|
||||
value: item.id,
|
||||
}),
|
||||
)}
|
||||
loading={staffInfo?.staffInfoLoading}
|
||||
disabled={!staffInfo?.basicDisciplineMajorList?.length}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="professionalLevelCode" label="职业等级">
|
||||
<Select
|
||||
placeholder="请选择职业等级"
|
||||
allowClear
|
||||
options={PROFESSIONAL_LEVEL_OPTIONS}
|
||||
onChange={(_val, option) => {
|
||||
form.setFieldValue("professionalLevelName", option?.label || undefined);
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="professionalLevelName" noStyle />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="evaluatorCertNo"
|
||||
label="证书编号"
|
||||
rules={[{ max: 50, message: "证书编号不能超过100个字符" }]}
|
||||
>
|
||||
<Input placeholder="请输入安全评价师证书编号" maxLength={50} showCount />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Form.Item name="educationTypeCode" label="学历类型">
|
||||
<Select
|
||||
|
|
@ -667,7 +672,10 @@ function StaffFormModal({
|
|||
allowClear
|
||||
options={EDUCATION_TYPE_OPTIONS}
|
||||
onChange={(_val, option) => {
|
||||
form.setFieldValue("educationTypeName", option?.label || undefined);
|
||||
form.setFieldValue(
|
||||
"educationTypeName",
|
||||
option?.label || undefined,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
|
@ -679,7 +687,7 @@ function StaffFormModal({
|
|||
placeholder="请选择学历层次"
|
||||
allowClear
|
||||
options={EDUCATION_LEVEL_OPTIONS}
|
||||
onChange={(val , option) => {
|
||||
onChange={(val, option) => {
|
||||
form.setFieldValue(
|
||||
"educationLevelName",
|
||||
option?.label || undefined,
|
||||
|
|
@ -687,17 +695,18 @@ function StaffFormModal({
|
|||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="educationLevelName" noStyle/>
|
||||
<Form.Item name="educationLevelName" noStyle />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="titleCode"
|
||||
label="职称"
|
||||
rules={[{ required: false, message: "请选择职称" }]}
|
||||
>
|
||||
<Select placeholder="请选择职称" allowClear options={TITLE_LEVEL_OPTIONS} />
|
||||
</Form.Item>
|
||||
<AttachmentUpload
|
||||
name="proofMaterialUrl"
|
||||
label="学历证明材料"
|
||||
extra={"最多上传5个pdf,doc,docx,图片格式文件"}
|
||||
accept={".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"}
|
||||
maxCount={5}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="registerEngineerFlag"
|
||||
|
|
@ -712,12 +721,128 @@ function StaffFormModal({
|
|||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="technicalDirectorFlag" label="是否技术负责人">
|
||||
<Select placeholder="请选择" allowClear options={[{ label: "是", value: true }, { label: "否", value: false }]} />
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
options={[
|
||||
{ label: "是", value: true },
|
||||
{ label: "否", value: false },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="processControlLeaderFlag" label="是否过程控制负责人">
|
||||
<Select placeholder="请选择" allowClear options={[{ label: "是", value: true }, { label: "否", value: false }]} />
|
||||
<Form.Item
|
||||
name="processControlLeaderFlag"
|
||||
label="是否过程控制负责人"
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
allowClear
|
||||
options={[
|
||||
{ label: "是", value: true },
|
||||
{ label: "否", value: false },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item label="职称信息" required>
|
||||
<Form.List
|
||||
name="titleCodeArr"
|
||||
initialValue={[{}]}
|
||||
rules={[{ required: true, message: "请添加职称信息" }]}
|
||||
>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
<Table
|
||||
dataSource={fields}
|
||||
rowKey={(field) => field.key}
|
||||
pagination={false}
|
||||
size="small"
|
||||
bordered
|
||||
locale={{ emptyText: "暂无职称信息,点击下方按钮添加" }}
|
||||
columns={[
|
||||
{
|
||||
title: "行业",
|
||||
width: 200,
|
||||
render: (_, field) => (
|
||||
<Form.Item
|
||||
name={[field.name, "industryCode"]}
|
||||
style={{ marginBottom: 0 }}
|
||||
rules={[
|
||||
{ required: true, message: "请选择行业" },
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择行业"
|
||||
allowClear
|
||||
options={QUALIFICATION_INDUSTRY_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "职称",
|
||||
width: 120,
|
||||
render: (_, field) => (
|
||||
<Form.Item
|
||||
name={[field.name, "titleCode"]}
|
||||
style={{ marginBottom: 0 }}
|
||||
rules={[
|
||||
{ required: true, message: "请选择职称" },
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择职称"
|
||||
allowClear
|
||||
options={TITLE_LEVEL_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,
|
||||
render: (_, field) => (
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
size="small"
|
||||
disabled={fields.length <= 1}
|
||||
onClick={() => remove(field.name)}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add()}
|
||||
block
|
||||
style={{ marginTop: 12 }}
|
||||
>
|
||||
添加职称信息
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
|
@ -726,7 +851,12 @@ function StaffFormModal({
|
|||
label="出版学术专著、专利、获奖、发表学术论文等"
|
||||
rules={[{ max: 500, message: "不能超过1000个字符" }]}
|
||||
>
|
||||
<Input.TextArea rows={2} placeholder="请输入" maxLength={500} showCount />
|
||||
<Input.TextArea
|
||||
rows={2}
|
||||
placeholder="请输入"
|
||||
maxLength={500}
|
||||
showCount
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
|
@ -735,27 +865,25 @@ function StaffFormModal({
|
|||
label="自我申报的专业能力及认定方式"
|
||||
rules={[{ max: 500, message: "不能超过1000个字符" }]}
|
||||
>
|
||||
<Input.TextArea rows={2} placeholder="请输入" maxLength={500} showCount />
|
||||
<Input.TextArea
|
||||
rows={2}
|
||||
placeholder="请输入"
|
||||
maxLength={500}
|
||||
showCount
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
name="workExperience"
|
||||
label="主要学习工作经历"
|
||||
<Form.Item name="workExperience" label="主要学习工作经历">
|
||||
<Input.TextArea
|
||||
rows={3}
|
||||
placeholder="请输入"
|
||||
maxLength={500}
|
||||
showCount
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
>
|
||||
<Input.TextArea rows={3} placeholder="请输入" maxLength={500} showCount />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<AttachmentUpload
|
||||
name="proofMaterialUrl"
|
||||
label="申报专业能力证明材料"
|
||||
extra={'最多上传5个pdf,doc,docx,图片格式文件'}
|
||||
accept={'.pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp'}
|
||||
maxCount={5}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="currentAddress"
|
||||
|
|
@ -783,21 +911,157 @@ function StaffFormModal({
|
|||
<Input placeholder="请输入毕业院校" maxLength={200} showCount />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="major"
|
||||
label="专业"
|
||||
rules={[{ max: 100, message: "专业不能超过100个字符" }]}
|
||||
>
|
||||
<Input placeholder="请输入专业" maxLength={100} showCount />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function StaffImportModal({ open, onCancel, onSuccess }) {
|
||||
const [result, setResult] = useState(null);
|
||||
const [errorMsg, setErrorMsg] = useState("");
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
const handleDownloadTemplate = async () => {
|
||||
try {
|
||||
const res = await Get("/safetyEval/org-personnel/import/template");
|
||||
const url = res?.data ;
|
||||
if (!url) {
|
||||
message.warning("未获取到模板下载地址");
|
||||
return;
|
||||
}
|
||||
const resolved = /^https?:\/\//i.test(url)
|
||||
? url
|
||||
: `${window.fileUrl || API_HOST}${url}`;
|
||||
const response = await fetch(resolved);
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const blob = await response.blob();
|
||||
const blobUrl = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = blobUrl;
|
||||
link.download = "人员导入模板.xlsx";
|
||||
link.style.display = "none";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(blobUrl);
|
||||
} catch (err) {
|
||||
message.error(err?.message || "下载模板失败");
|
||||
}
|
||||
};
|
||||
|
||||
const customRequest = async (options) => {
|
||||
const { file, onSuccess: onDone, onError } = options;
|
||||
setUploading(true);
|
||||
setResult(null);
|
||||
setErrorMsg("");
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
const headers = { token: sessionStorage.getItem("token") || "" };
|
||||
const orgInfoId = sessionStorage.getItem("orgInfoId");
|
||||
if (orgInfoId) headers.orgInfoId = String(orgInfoId);
|
||||
const resp = await fetch(
|
||||
`${API_HOST}/safetyEval/org-personnel/import`,
|
||||
{ method: "POST", headers, body: formData },
|
||||
);
|
||||
const json = await resp.json();
|
||||
if (json && json.success !== false) {
|
||||
onDone(json.data, file);
|
||||
setResult(json.data || {});
|
||||
if ((json.data?.successCount || 0) > 0) onSuccess?.();
|
||||
} else {
|
||||
onError(new Error(json?.errMessage || json?.message || "导入失败"));
|
||||
setErrorMsg(json?.errMessage || json?.message || "导入失败");
|
||||
}
|
||||
} catch (err) {
|
||||
onError(err);
|
||||
setErrorMsg(err?.message || "导入失败");
|
||||
} finally {
|
||||
setUploading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const errorList = result?.errors || [];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
title="导入人员"
|
||||
width={680}
|
||||
onCancel={onCancel}
|
||||
footer={<Button onClick={onCancel}>关闭</Button>}
|
||||
>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Button type="link" size="small" onClick={handleDownloadTemplate}>
|
||||
下载导入模板
|
||||
</Button>
|
||||
</div>
|
||||
<Upload.Dragger
|
||||
accept=".xlsx,.xls"
|
||||
maxCount={1}
|
||||
multiple={false}
|
||||
customRequest={customRequest}
|
||||
disabled={uploading}
|
||||
beforeUpload={(file) => {
|
||||
const ext =
|
||||
"." + (file.name.split(".").pop() || "").toLowerCase();
|
||||
if (![".xlsx", ".xls"].includes(ext)) {
|
||||
message.error("仅支持 .xlsx / .xls 格式文件");
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
onRemove={() => {
|
||||
setResult(null);
|
||||
setErrorMsg("");
|
||||
return true;
|
||||
}}
|
||||
>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<InboxOutlined />
|
||||
</p>
|
||||
<p className="ant-upload-text">点击或拖拽文件到此区域上传</p>
|
||||
<p className="ant-upload-hint">仅支持 .xlsx / .xls 格式文件</p>
|
||||
</Upload.Dragger>
|
||||
|
||||
{errorMsg && (
|
||||
<Alert
|
||||
style={{ marginTop: 12 }}
|
||||
type="error"
|
||||
showIcon
|
||||
message="导入失败"
|
||||
description={errorMsg}
|
||||
/>
|
||||
)}
|
||||
{result && (
|
||||
<Alert
|
||||
style={{ marginTop: 12 }}
|
||||
type={result.failCount > 0 ? "warning" : "success"}
|
||||
showIcon
|
||||
message={`成功导入 ${result.successCount || 0} 条,失败 ${result.failCount || 0} 条`}
|
||||
/>
|
||||
)}
|
||||
{errorList.length > 0 && (
|
||||
<Table
|
||||
style={{ marginTop: 12 }}
|
||||
size="small"
|
||||
rowKey={(r, i) => i}
|
||||
pagination={false}
|
||||
scroll={{ y: 240 }}
|
||||
dataSource={errorList}
|
||||
columns={[
|
||||
{ title: "行号", dataIndex: "rowIndex", width: 70 },
|
||||
{ title: "账号", dataIndex: "account", ellipsis: true },
|
||||
{ title: "姓名", dataIndex: "userName", ellipsis: true },
|
||||
{ title: "异常原因", dataIndex: "errorMessage", ellipsis: true },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default Connect(
|
||||
[NS_STAFF_INFO, NS_ORG_DEPARTMENT],
|
||||
true,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
import { CloseOutlined } from "@ant-design/icons";
|
||||
import { NS_STAFF_INFO, NS_ORG_DEPARTMENT } from "~/enumerate/namespace";
|
||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP, PROFESSIONAL_LEVEL_OPTIONS_MAP, TITLE_LEVEL_MAP } from "~/enumerate/enterpriseOptions";
|
||||
import { QUALIFICATION_INDUSTRY_OPTIONS_MAP, TITLE_LEVEL_MAP } from "~/enumerate/enterpriseOptions";
|
||||
|
||||
|
||||
|
||||
|
|
@ -129,15 +129,7 @@ const StaffPickerModal = (props) => {
|
|||
return status || "-";
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "职业等级",
|
||||
dataIndex: "professionalLevelCode",
|
||||
ellipsis: true,
|
||||
render: (code) => {
|
||||
const status = PROFESSIONAL_LEVEL_OPTIONS_MAP[code];
|
||||
return status || "-";
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
title: "职称",
|
||||
dataIndex: "titleCode",
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ const InternalReviewContent = ({ readOnly, ...props }) => {
|
|||
setRows(
|
||||
reviewItems.map((item) => ({
|
||||
...item,
|
||||
reviewConclusion: item.reviewConclusion || "conform_to",
|
||||
_key: item.id || item.reviewDimensionCode,
|
||||
})),
|
||||
);
|
||||
|
|
@ -141,7 +142,7 @@ const InternalReviewContent = ({ readOnly, ...props }) => {
|
|||
width: 120,
|
||||
render: (val, _, idx) => (
|
||||
<Select
|
||||
value={val || 'conform_to'}
|
||||
value={val}
|
||||
size="small"
|
||||
style={{ width: 130 }}
|
||||
allowClear
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ const TechnicalReviewContent = ({ readOnly, ...props }) => {
|
|||
setRows(
|
||||
reviewItems.map((item) => ({
|
||||
...item,
|
||||
reviewConclusion: item.reviewConclusion || "conform_to",
|
||||
_key: item.id || item.reviewDimensionCode,
|
||||
})),
|
||||
);
|
||||
|
|
@ -140,7 +141,7 @@ const TechnicalReviewContent = ({ readOnly, ...props }) => {
|
|||
width: 120,
|
||||
render: (val, _, idx) => (
|
||||
<Select
|
||||
value={val || 'conform_to'}
|
||||
value={val}
|
||||
size="small"
|
||||
style={{ width: 130 }}
|
||||
allowClear
|
||||
|
|
|
|||
|
|
@ -372,7 +372,7 @@ function OrgAccountPage(props) {
|
|||
<Descriptions.Item label="属地">
|
||||
{detail.districtName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="安全生产监管行业类别">
|
||||
<Descriptions.Item label="执业资质范围">
|
||||
{detail.safetyIndustryCategoryName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="经营地址" span={2}>
|
||||
|
|
@ -449,7 +449,7 @@ function OrgAccountPage(props) {
|
|||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="safetyIndustryCategoryName"
|
||||
label="安全生产监管行业类别"
|
||||
label="执业资质范围"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
|
|
|||
Loading…
Reference in New Issue