feat
parent
0c9e2d62d3
commit
950980d48b
|
|
@ -1,55 +1,53 @@
|
||||||
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import {
|
import { Get } from "@cqsjjb/jjb-common-lib/http";
|
||||||
fromPageResponse,
|
|
||||||
fromSingleResponse,
|
|
||||||
fromStaffCertForm,
|
|
||||||
toPageQuery,
|
|
||||||
toStaffCertForm,
|
|
||||||
} from "../enterpriseInfo/adapter";
|
|
||||||
import { apiGet, apiPost, apiPostDelete, safeAction, safePageResult } from "../enterpriseInfo/http";
|
|
||||||
|
|
||||||
export const staffCertificateList = declareRequest("staffCertificateLoading", safePageResult(async (params) => {
|
export const staffCertificateList = declareRequest(
|
||||||
const query = toPageQuery(params, {
|
"staffCertificateLoading",
|
||||||
eqStaffId: "personnelId",
|
"Get > /safetyEval/org-personnel-cert/page",
|
||||||
likeCertNo: "certNo",
|
"staffCertificateList: [] | res.data || [] & staffCertificateTotal: 0 | res.data?.total || 0",
|
||||||
});
|
);
|
||||||
const res = await apiGet("/safetyEval/org-personnel-cert/page", query);
|
|
||||||
return fromPageResponse(res, toStaffCertForm);
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const staffCertificateInfo = declareRequest("staffCertificateLoading", safeAction(async (params) => {
|
export const staffCertificateInfo = declareRequest(
|
||||||
const res = await apiGet("/safetyEval/org-personnel-cert/get", { id: params.id });
|
"staffCertificateLoading",
|
||||||
return fromSingleResponse(res, toStaffCertForm);
|
"Get > /safetyEval/org-personnel-cert/get",
|
||||||
}));
|
"staffCertificateDetail: {} | res.data || {}",
|
||||||
|
);
|
||||||
|
|
||||||
export const staffCertificateAdd = declareRequest("staffCertificateLoading", safeAction(async (values) => {
|
export const staffCertificateAdd = declareRequest(
|
||||||
const res = await apiPost("/safetyEval/org-personnel-cert/save", fromStaffCertForm(values));
|
"staffCertificateLoading",
|
||||||
return fromSingleResponse(res, toStaffCertForm);
|
"Post > @/safetyEval/org-personnel-cert/save",
|
||||||
}));
|
);
|
||||||
|
|
||||||
export const staffCertificateEdit = declareRequest("staffCertificateLoading", safeAction(async (values) => {
|
export const staffCertificateEdit = declareRequest(
|
||||||
const res = await apiPost("/safetyEval/org-personnel-cert/modify", fromStaffCertForm(values));
|
"staffCertificateLoading",
|
||||||
return fromSingleResponse(res, toStaffCertForm);
|
"Post > @/safetyEval/org-personnel-cert/modify",
|
||||||
}));
|
);
|
||||||
|
|
||||||
export const staffCertificateRemove = declareRequest("staffCertificateLoading", safeAction(async ({ id }) => {
|
export const staffCertificateRemove = declareRequest(
|
||||||
return apiPostDelete("/safetyEval/org-personnel-cert/delete", id);
|
"staffCertificateLoading",
|
||||||
}));
|
"Post > @/safetyEval/org-personnel-cert/delete",
|
||||||
|
);
|
||||||
|
|
||||||
|
/** StaffViewModal 直接调用的辅助函数 — 获取人员下所有证书列表 */
|
||||||
export async function fetchStaffCertListByPersonnelId(personnelId) {
|
export async function fetchStaffCertListByPersonnelId(personnelId) {
|
||||||
const query = toPageQuery({
|
try {
|
||||||
pageIndex: 1,
|
const res = await Get("/safetyEval/org-personnel-cert/page", {
|
||||||
pageSize: 999,
|
current: 1,
|
||||||
eqStaffId: personnelId,
|
size: 999,
|
||||||
}, {
|
personnelId,
|
||||||
eqStaffId: "personnelId",
|
});
|
||||||
});
|
return res?.data || [];
|
||||||
const res = await apiGet("/safetyEval/org-personnel-cert/page", query);
|
} catch {
|
||||||
const page = fromPageResponse(res, toStaffCertForm);
|
return [];
|
||||||
return page?.data || [];
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** StaffViewModal 直接调用的辅助函数 — 获取证书详情 */
|
||||||
export async function fetchStaffCertDetail({ id }) {
|
export async function fetchStaffCertDetail({ id }) {
|
||||||
const res = await apiGet("/safetyEval/org-personnel-cert/get", { id });
|
try {
|
||||||
return fromSingleResponse(res, toStaffCertForm);
|
const res = await Get("/safetyEval/org-personnel-cert/get", { id });
|
||||||
}
|
return res?.data || null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,42 +1,29 @@
|
||||||
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import {
|
|
||||||
fromPageResponse,
|
|
||||||
fromResignAuditForm,
|
|
||||||
fromSingleResponse,
|
|
||||||
toChangeLogForm,
|
|
||||||
toPageQuery,
|
|
||||||
toResignApplyForm,
|
|
||||||
toStaffChangeSummary,
|
|
||||||
} from "../enterpriseInfo/adapter";
|
|
||||||
import { apiGet, apiPost, apiPostDelete, safeAction, safePageResult } from "../enterpriseInfo/http";
|
|
||||||
|
|
||||||
export const staffChangeLogStaffList = declareRequest("staffChangeLogLoading", safePageResult(async (params) => {
|
export const staffChangeLogStaffList = declareRequest(
|
||||||
const query = toPageQuery(params, {
|
"staffChangeLogLoading",
|
||||||
likeAccount: "account",
|
"Get > /safetyEval/org-personnel/page",
|
||||||
likeStaffName: "userName",
|
"staffChangeLogStaffList: [] | res.data || [] & staffChangeLogStaffTotal: 0 | res.total || 0",
|
||||||
employmentStatus: "employmentStatusCode",
|
);
|
||||||
resignAuditStatus: "resignAuditStatus",
|
|
||||||
});
|
|
||||||
const res = await apiGet("/safetyEval/org-personnel/page", query);
|
|
||||||
return fromPageResponse(res, toStaffChangeSummary);
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const staffChangeLogList = declareRequest("staffChangeLogLoading", safePageResult(async (params) => {
|
export const staffChangeLogList = declareRequest(
|
||||||
const query = toPageQuery(params, { eqStaffId: "personnelId" });
|
"staffChangeLogLoading",
|
||||||
const res = await apiGet("/safetyEval/org-personnel-change/page", query);
|
"Get > /safetyEval/org-personnel-change/page",
|
||||||
return fromPageResponse(res, toChangeLogForm);
|
"staffChangeLogChangeList: [] | res.data || [] & staffChangeLogChangeTotal: 0 | res.total || 0",
|
||||||
}));
|
);
|
||||||
|
|
||||||
export const staffChangeLogRemove = declareRequest("staffChangeLogLoading", safeAction(async ({ id }) => {
|
export const staffChangeLogRemove = declareRequest(
|
||||||
return apiPostDelete("/safetyEval/org-personnel-change/delete", id);
|
"staffChangeLogLoading",
|
||||||
}));
|
"Post > @/safetyEval/org-personnel-change/delete",
|
||||||
|
);
|
||||||
|
|
||||||
export const staffResignationAudit = declareRequest("staffChangeLogLoading", safeAction(async (params) => {
|
export const staffResignationAudit = declareRequest(
|
||||||
const res = await apiPost("/safetyEval/org-resign-apply/modify", fromResignAuditForm(params));
|
"staffChangeLogLoading",
|
||||||
return fromSingleResponse(res, toResignApplyForm);
|
"Post > @/safetyEval/org-resign-apply/modify",
|
||||||
}));
|
);
|
||||||
|
|
||||||
export const staffResignationInfo = declareRequest("staffChangeLogLoading", safeAction(async (params) => {
|
export const staffResignationInfo = declareRequest(
|
||||||
const res = await apiGet("/safetyEval/org-resign-apply/get", { id: params.id });
|
"staffChangeLogLoading",
|
||||||
return fromSingleResponse(res, toResignApplyForm);
|
"Get > /safetyEval/org-resign-apply/get",
|
||||||
}));
|
"staffResignationDetail: {} | res.data || {}",
|
||||||
|
);
|
||||||
|
|
@ -1,43 +1,33 @@
|
||||||
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import {
|
|
||||||
fromPageResponse,
|
|
||||||
fromSingleResponse,
|
|
||||||
fromStaffForm,
|
|
||||||
toPageQuery,
|
|
||||||
toStaffForm,
|
|
||||||
} from "../enterpriseInfo/adapter";
|
|
||||||
import { apiGet, apiPost, apiPostDelete, safeAction, safePageResult } from "../enterpriseInfo/http";
|
|
||||||
|
|
||||||
export const staffInfoList = declareRequest("staffInfoLoading", safePageResult(async (params) => {
|
export const staffInfoList = declareRequest(
|
||||||
const query = toPageQuery(params, {
|
"staffInfoLoading",
|
||||||
likeStaffName: "userName",
|
"Get > /safetyEval/org-personnel/page",
|
||||||
eqDeptId: "deptId",
|
"staffInfoList: [] | res.data || [] & staffInfoTotal: 0 | res.data?.total || 0",
|
||||||
eqPositionId: "postId",
|
);
|
||||||
});
|
|
||||||
const res = await apiGet("/safetyEval/org-personnel/page", query);
|
|
||||||
return fromPageResponse(res, toStaffForm);
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const staffInfoGet = declareRequest("staffInfoLoading", safeAction(async (params) => {
|
export const staffInfoGet = declareRequest(
|
||||||
const res = await apiGet("/safetyEval/org-personnel/get", { id: params.id });
|
"staffInfoLoading",
|
||||||
return fromSingleResponse(res, toStaffForm);
|
"Get > /safetyEval/org-personnel/get",
|
||||||
}));
|
"staffInfoDetail: {} | res.data || {}",
|
||||||
|
);
|
||||||
|
|
||||||
export const staffInfoAdd = declareRequest("staffInfoLoading", safeAction(async (values) => {
|
export const staffInfoAdd = declareRequest(
|
||||||
const res = await apiPost("/safetyEval/org-personnel/save", fromStaffForm(values));
|
"staffInfoLoading",
|
||||||
return fromSingleResponse(res, toStaffForm);
|
"Post > @/safetyEval/org-personnel/save",
|
||||||
}));
|
);
|
||||||
|
|
||||||
export const staffInfoEdit = declareRequest("staffInfoLoading", safeAction(async (values) => {
|
export const staffInfoEdit = declareRequest(
|
||||||
const res = await apiPost("/safetyEval/org-personnel/modify", fromStaffForm(values));
|
"staffInfoLoading",
|
||||||
return fromSingleResponse(res, toStaffForm);
|
"Post > @/safetyEval/org-personnel/modify",
|
||||||
}));
|
);
|
||||||
|
|
||||||
export const staffInfoRemove = declareRequest("staffInfoLoading", safeAction(async ({ id }) => {
|
export const staffInfoRemove = declareRequest(
|
||||||
return apiPostDelete("/safetyEval/org-personnel/delete", id);
|
"staffInfoLoading",
|
||||||
}));
|
"Post > @/safetyEval/org-personnel/delete",
|
||||||
|
);
|
||||||
|
|
||||||
/** 后端暂未提供重置密码接口 */
|
export const staffInfoResetPassword = declareRequest(
|
||||||
export const staffInfoResetPassword = declareRequest("staffInfoLoading", safeAction(async ({ id }) => {
|
"staffInfoLoading",
|
||||||
return apiPost(`/safetyEval/org-personnel/reset-password?id=${id}`);
|
"Get > /safetyEval/org-personnel/reset-password",
|
||||||
}));
|
);
|
||||||
|
|
@ -1,28 +1,18 @@
|
||||||
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import {
|
|
||||||
fromPageResponse,
|
|
||||||
fromResignApplyForm,
|
|
||||||
fromSingleResponse,
|
|
||||||
toPageQuery,
|
|
||||||
toResignApplyForm,
|
|
||||||
} from "../enterpriseInfo/adapter";
|
|
||||||
import { apiGet, apiPost, safeAction, safePageResult } from "../enterpriseInfo/http";
|
|
||||||
|
|
||||||
export const staffResignationApplyList = declareRequest("staffResignationApplyLoading", safePageResult(async (params) => {
|
export const staffResignationApplyList = declareRequest(
|
||||||
const query = toPageQuery(params, {
|
"staffResignationApplyLoading",
|
||||||
likeStaffName: "applicantName",
|
"Get > /safetyEval/org-resign-apply/page",
|
||||||
auditStatus: "auditStatusCode",
|
"staffResignationApplyList: [] | res.data || [] & staffResignationApplyTotal: 0 | res.total || 0",
|
||||||
});
|
);
|
||||||
const res = await apiGet("/safetyEval/org-resign-apply/page", query);
|
|
||||||
return fromPageResponse(res, toResignApplyForm);
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const staffResignationApplyInfo = declareRequest("staffResignationApplyLoading", safeAction(async (params) => {
|
export const staffResignationApplyInfo = declareRequest(
|
||||||
const res = await apiGet("/safetyEval/org-resign-apply/get", { id: params.id });
|
"staffResignationApplyLoading",
|
||||||
return fromSingleResponse(res, toResignApplyForm);
|
"Get > /safetyEval/org-resign-apply/get",
|
||||||
}));
|
"resignApplyDetail: {} | res.data || {}",
|
||||||
|
);
|
||||||
|
|
||||||
export const staffResignationApplyAdd = declareRequest("staffResignationApplyLoading", safeAction(async (values) => {
|
export const staffResignationApplyAdd = declareRequest(
|
||||||
const res = await apiPost("/safetyEval/org-resign-apply/save", fromResignApplyForm(values));
|
"staffResignationApplyLoading",
|
||||||
return fromSingleResponse(res, toResignApplyForm);
|
"Post > @/safetyEval/org-resign-apply/save",
|
||||||
}));
|
);
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import { Button, Empty, Form, Input, message, Modal, Row, Col, Select, Space, Tree } from "antd";
|
import { Button, Empty, Form, Input, message, Modal, Row, Col, Select, Space, Tree } from "antd";
|
||||||
|
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import AddIcon from "zy-react-library/components/Icon/AddIcon";
|
import AddIcon from "zy-react-library/components/Icon/AddIcon";
|
||||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||||
|
|
@ -271,10 +272,10 @@ function DepartmentPositionPage(props) {
|
||||||
title: "操作",
|
title: "操作",
|
||||||
width: 160,
|
width: 160,
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space>
|
<TableAction>
|
||||||
<Button type="link" onClick={() => openPositionModal(record)}>编辑</Button>
|
<Button type="link" onClick={() => openPositionModal(record)}>编辑</Button>
|
||||||
<Button danger type="link" onClick={() => onDeletePosition(record.id)}>删除</Button>
|
<Button danger type="link" onClick={() => onDeletePosition(record.id)}>删除</Button>
|
||||||
</Space>
|
</TableAction>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,30 @@
|
||||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import { Button, Descriptions, Form, Input, message, Modal, Select, Space, Typography } from "antd";
|
import { Button, Descriptions, Form, Input, message, Modal, Select, Space, Table, Tag, Typography } from "antd";
|
||||||
|
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||||
import Search from "zy-react-library/components/Search";
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
||||||
import Table from "zy-react-library/components/Table";
|
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
|
||||||
import useTable from "zy-react-library/hooks/useTable";
|
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
||||||
|
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||||
import { NS_STAFF_CHANGE_LOG, NS_STAFF_INFO } from "~/enumerate/namespace";
|
import { NS_STAFF_CHANGE_LOG, NS_STAFF_INFO } from "~/enumerate/namespace";
|
||||||
import { safeListRequest, safeRequest } from "~/utils";
|
import {
|
||||||
import { CHANGE_COUNT_STYLE, formSelectField, getChangeCount, getEmploymentStatusLabel, getResignAuditStatusLabel } from "~/utils/enterpriseForm";
|
CHANGE_COUNT_STYLE,
|
||||||
|
RESIGN_AUDIT_STATUS_LABEL,
|
||||||
|
AUDIT_STATUS_COLOR,
|
||||||
|
} from "~/utils/enterpriseForm";
|
||||||
|
|
||||||
|
const { router } = tools;
|
||||||
|
|
||||||
const EMPLOYMENT_STATUS = { 1: "在职", 2: "离职" };
|
const EMPLOYMENT_STATUS_OPTIONS = [
|
||||||
const RESIGN_AUDIT_STATUS = { 0: "未审核", 1: "已审核", 2: "已退回" };
|
{ value: 1, label: "在职" },
|
||||||
const SEARCH_COL = { xs: 24, sm: 12, md: 8, lg: 6 };
|
{ value: 2, label: "离职" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const RESIGN_AUDIT_OPTIONS = Object.entries(RESIGN_AUDIT_STATUS_LABEL).map(([value, label]) => ({
|
||||||
|
value: Number(value),
|
||||||
|
label,
|
||||||
|
}));
|
||||||
|
|
||||||
function PersonnelChangePage(props) {
|
function PersonnelChangePage(props) {
|
||||||
const [viewMode, setViewMode] = useState("list");
|
const [viewMode, setViewMode] = useState("list");
|
||||||
|
|
@ -22,27 +34,34 @@ function PersonnelChangePage(props) {
|
||||||
const [resignInfo, setResignInfo] = useState({});
|
const [resignInfo, setResignInfo] = useState({});
|
||||||
const [rejectReason, setRejectReason] = useState("");
|
const [rejectReason, setRejectReason] = useState("");
|
||||||
const [searchForm] = Form.useForm();
|
const [searchForm] = Form.useForm();
|
||||||
const [logForm] = Form.useForm();
|
|
||||||
|
|
||||||
const { tableProps, getData } = useTable(safeListRequest(props.staffChangeLogStaffList), {
|
const { staffChangeLog } = props;
|
||||||
form: searchForm,
|
const {
|
||||||
});
|
staffChangeLogStaffList: staffList,
|
||||||
|
staffChangeLogStaffTotal: staffTotal,
|
||||||
|
staffChangeLogLoading: loading,
|
||||||
|
staffChangeLogChangeList: changeList,
|
||||||
|
staffChangeLogChangeTotal: changeTotal,
|
||||||
|
} = staffChangeLog || {};
|
||||||
|
|
||||||
const { tableProps: logTableProps, getData: getLogData } = useTable(
|
const handleSearch = () => {
|
||||||
safeListRequest(props.staffChangeLogList),
|
props.staffChangeLogStaffList({ ...router.query });
|
||||||
{
|
};
|
||||||
form: logForm,
|
|
||||||
transform: () => ({ eqStaffId: selectedStaff?.staffId }),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getData();
|
searchForm.setFieldsValue(router.query);
|
||||||
|
handleSearch();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleLogSearch = () => {
|
||||||
|
if (selectedStaff?.staffId) {
|
||||||
|
props.staffChangeLogList({ eqStaffId: selectedStaff.staffId });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (viewMode === "log" && selectedStaff?.staffId) {
|
if (viewMode === "log" && selectedStaff?.staffId) {
|
||||||
getLogData();
|
handleLogSearch();
|
||||||
}
|
}
|
||||||
}, [viewMode, selectedStaff?.staffId]);
|
}, [viewMode, selectedStaff?.staffId]);
|
||||||
|
|
||||||
|
|
@ -56,9 +75,8 @@ function PersonnelChangePage(props) {
|
||||||
const res = await props.staffInfoRemove?.({ id: record.staffId || record.id });
|
const res = await props.staffInfoRemove?.({ id: record.staffId || record.id });
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
message.success("删除成功");
|
message.success("删除成功");
|
||||||
getData();
|
handleSearch();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
message.error(res?.message || "删除失败");
|
message.error(res?.message || "删除失败");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -67,26 +85,23 @@ function PersonnelChangePage(props) {
|
||||||
|
|
||||||
const openResignAudit = async (record) => {
|
const openResignAudit = async (record) => {
|
||||||
try {
|
try {
|
||||||
const res = await safeRequest(props.staffResignationInfo, { id: record.resignApplyId });
|
const res = await props.staffResignationInfo({ id: record.resignApplyId });
|
||||||
setResignInfo(res?.data || {});
|
const data = res?.data || {};
|
||||||
|
setResignInfo(data);
|
||||||
setSelectedStaff(record);
|
setSelectedStaff(record);
|
||||||
setRejectReason("");
|
setRejectReason("");
|
||||||
setAuditModalOpen(true);
|
setAuditModalOpen(true);
|
||||||
}
|
} catch {
|
||||||
catch (err) {
|
|
||||||
console.warn("[PersonnelChange] openResignAudit failed:", err);
|
|
||||||
message.error("获取离职申请详情失败");
|
message.error("获取离职申请详情失败");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const openResignView = async (record) => {
|
const openResignView = async (record) => {
|
||||||
try {
|
try {
|
||||||
const res = await safeRequest(props.staffResignationInfo, { id: record.resignApplyId });
|
const res = await props.staffResignationInfo({ id: record.resignApplyId });
|
||||||
setResignInfo(res?.data || {});
|
setResignInfo(res?.data || {});
|
||||||
setViewResignOpen(true);
|
setViewResignOpen(true);
|
||||||
}
|
} catch {
|
||||||
catch (err) {
|
|
||||||
console.warn("[PersonnelChange] openResignView failed:", err);
|
|
||||||
message.error("获取离职申请详情失败");
|
message.error("获取离职申请详情失败");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -99,39 +114,121 @@ function PersonnelChangePage(props) {
|
||||||
try {
|
try {
|
||||||
const res = await props.staffResignationAudit({
|
const res = await props.staffResignationAudit({
|
||||||
id: resignInfo.id,
|
id: resignInfo.id,
|
||||||
auditStatus: passed ? 1 : 2,
|
auditStatusCode: passed ? 1 : 2,
|
||||||
|
auditStatusName: passed ? "已审核" : "已退回",
|
||||||
rejectReason: passed ? undefined : rejectReason,
|
rejectReason: passed ? undefined : rejectReason,
|
||||||
});
|
});
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
message.success(passed ? "审核通过" : "已退回");
|
message.success(passed ? "审核通过" : "已退回");
|
||||||
setAuditModalOpen(false);
|
setAuditModalOpen(false);
|
||||||
getData();
|
handleSearch();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
message.error(res?.message || "审核操作失败");
|
message.error(res?.message || "审核操作失败");
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
catch (err) {
|
|
||||||
console.warn("[PersonnelChange] submitAudit failed:", err);
|
|
||||||
message.error("审核操作失败");
|
message.error("审核操作失败");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const staffColumns = [
|
||||||
|
{ title: "账户", dataIndex: "account", width: 130 },
|
||||||
|
{ title: "姓名", dataIndex: "userName", width: 100 },
|
||||||
|
{ title: "部门", dataIndex: "deptName", width: 120 },
|
||||||
|
{
|
||||||
|
title: "信息变更数",
|
||||||
|
dataIndex: "changeCount",
|
||||||
|
width: 110,
|
||||||
|
render: (_, record) => {
|
||||||
|
const n = Number(record.changeCount ?? 0);
|
||||||
|
const style = n > 0 ? CHANGE_COUNT_STYLE.active : CHANGE_COUNT_STYLE.zero;
|
||||||
|
if (n <= 0) {
|
||||||
|
return <span style={style}>{n}</span>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Typography.Link
|
||||||
|
style={style}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedStaff(record);
|
||||||
|
setViewMode("log");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{n}
|
||||||
|
</Typography.Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "就职状态",
|
||||||
|
dataIndex: "employmentStatusCode",
|
||||||
|
width: 90,
|
||||||
|
render: (_, record) => {
|
||||||
|
const code = record.employmentStatusCode ?? record.employmentStatus;
|
||||||
|
return <Tag color={code === 2 ? "error" : "success"}>{code === 2 ? "离职" : "在职"}</Tag>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "离职申请审核状态",
|
||||||
|
dataIndex: "resignAuditStatus",
|
||||||
|
width: 140,
|
||||||
|
render: (_, record) => {
|
||||||
|
const code = record.resignAuditStatus ?? record.auditStatus ?? record.auditStatusCode;
|
||||||
|
const label = RESIGN_AUDIT_STATUS_LABEL[code] ?? "-";
|
||||||
|
return <Tag color={AUDIT_STATUS_COLOR[code]}>{label}</Tag>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
width: 200,
|
||||||
|
fixed: "right",
|
||||||
|
render: (_, record) => (
|
||||||
|
<TableAction>
|
||||||
|
<Button type="link" size="small" onClick={() => openResignView(record)}>
|
||||||
|
查看
|
||||||
|
</Button>
|
||||||
|
{record.resignApplyId && Number(record.resignAuditStatus) === 0 && (
|
||||||
|
<Button type="link" size="small" onClick={() => openResignAudit(record)}>
|
||||||
|
离职审核
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button danger type="link" size="small" onClick={() => onDelete(record)}>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</TableAction>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const logColumns = [
|
||||||
|
{ title: "变更事项", dataIndex: "changeItem", width: 200 },
|
||||||
|
{ title: "变更时间", dataIndex: "changeTime", width: 160 },
|
||||||
|
{ title: "操作人", dataIndex: "createBy", width: 100 },
|
||||||
|
];
|
||||||
|
|
||||||
if (viewMode === "log") {
|
if (viewMode === "log") {
|
||||||
return (
|
return (
|
||||||
<PageLayout title="人员变更次数">
|
<PageLayout title="人员变更次数">
|
||||||
<Button style={{ marginBottom: 16 }} onClick={() => setViewMode("list")}>返回</Button>
|
<Button style={{ marginBottom: 16 }} onClick={() => setViewMode("list")}>
|
||||||
<div style={{ marginBottom: 8 }}>
|
返回
|
||||||
人员:
|
</Button>
|
||||||
{selectedStaff?.staffName}
|
<div style={{ marginBottom: 8 }}>人员:{selectedStaff?.staffName || selectedStaff?.userName}</div>
|
||||||
</div>
|
|
||||||
<Table
|
<Table
|
||||||
columns={[
|
rowKey="id"
|
||||||
{ title: "变更事项", dataIndex: "changeItem" },
|
columns={logColumns}
|
||||||
{ title: "变更时间", dataIndex: "changeTime" },
|
dataSource={changeList}
|
||||||
{ title: "操作人", dataIndex: "createBy" },
|
scroll={{ y: props.scrollY }}
|
||||||
]}
|
loading={loading}
|
||||||
{...logTableProps}
|
pagination={{
|
||||||
|
total: changeTotal,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (t) => `共 ${t} 条`,
|
||||||
|
current: router.query.current || 1,
|
||||||
|
pageSize: router.query.size || 10,
|
||||||
|
onChange: (page, pageSize) => {
|
||||||
|
router.query = { ...router.query, current: page, size: pageSize };
|
||||||
|
handleLogSearch();
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
|
|
@ -148,82 +245,60 @@ function PersonnelChangePage(props) {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Search
|
<SearchForm
|
||||||
|
style={{ marginBottom: 24 }}
|
||||||
form={searchForm}
|
form={searchForm}
|
||||||
options={[
|
loading={loading}
|
||||||
{ name: "likeAccount", label: "账户", placeholder: "请输入账户", colProps: SEARCH_COL },
|
formLine={[
|
||||||
{ name: "likeStaffName", label: "姓名", placeholder: "请输入姓名", colProps: SEARCH_COL },
|
<Form.Item key="account" name="account">
|
||||||
formSelectField(
|
<ControlWrapper.Input label="账户" placeholder="请输入" allowClear />
|
||||||
"employmentStatus",
|
</Form.Item>,
|
||||||
"就职状态",
|
<Form.Item key="userName" name="userName">
|
||||||
Object.entries(EMPLOYMENT_STATUS).map(([value, label]) => ({ label, value: Number(value) })),
|
<ControlWrapper.Input label="姓名" placeholder="请输入" allowClear />
|
||||||
{ colProps: SEARCH_COL },
|
</Form.Item>,
|
||||||
),
|
<Form.Item key="employmentStatusCode" name="employmentStatusCode">
|
||||||
formSelectField(
|
<ControlWrapper.Select label="就职状态" placeholder="请选择" allowClear style={{ width: "100%" }}>
|
||||||
"resignAuditStatus",
|
{EMPLOYMENT_STATUS_OPTIONS.map((o) => (
|
||||||
"复核状态",
|
<Select.Option key={o.value} value={o.value}>{o.label}</Select.Option>
|
||||||
Object.entries(RESIGN_AUDIT_STATUS).map(([value, label]) => ({ label, value: Number(value) })),
|
))}
|
||||||
{ colProps: SEARCH_COL, placeholder: "请选择复核状态" },
|
</ControlWrapper.Select>
|
||||||
),
|
</Form.Item>,
|
||||||
|
<Form.Item key="resignAuditStatus" name="resignAuditStatus">
|
||||||
|
<ControlWrapper.Select label="复核状态" placeholder="请选择" allowClear style={{ width: "100%" }}>
|
||||||
|
{RESIGN_AUDIT_OPTIONS.map((o) => (
|
||||||
|
<Select.Option key={o.value} value={o.value}>{o.label}</Select.Option>
|
||||||
|
))}
|
||||||
|
</ControlWrapper.Select>
|
||||||
|
</Form.Item>,
|
||||||
]}
|
]}
|
||||||
onFinish={getData}
|
onReset={() => {
|
||||||
|
router.query = { ...router.query, current: 1, size: 10 };
|
||||||
|
handleSearch();
|
||||||
|
}}
|
||||||
|
onFinish={() => {
|
||||||
|
router.query = { ...router.query, current: 1, size: 10 };
|
||||||
|
handleSearch();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
columns={[
|
rowKey={(record) => record.id || record.staffId}
|
||||||
{ title: "账户", dataIndex: "account", width: 130 },
|
columns={staffColumns}
|
||||||
{ title: "姓名", dataIndex: "staffName", width: 100 },
|
dataSource={staffList}
|
||||||
{ title: "部门", dataIndex: "deptName", width: 120 },
|
scroll={{ y: props.scrollY }}
|
||||||
{
|
loading={loading}
|
||||||
title: "信息变更数",
|
pagination={{
|
||||||
dataIndex: "changeCount",
|
total: staffTotal,
|
||||||
width: 110,
|
showSizeChanger: true,
|
||||||
render: (_, record) => {
|
showQuickJumper: true,
|
||||||
const n = getChangeCount(record);
|
showTotal: (t) => `共 ${t} 条`,
|
||||||
const style = n > 0 ? CHANGE_COUNT_STYLE.active : CHANGE_COUNT_STYLE.zero;
|
current: router.query.current || 1,
|
||||||
if (n <= 0) {
|
pageSize: router.query.size || 10,
|
||||||
return <span style={style}>{n}</span>;
|
onChange: (page, pageSize) => {
|
||||||
}
|
router.query = { ...router.query, current: page, size: pageSize };
|
||||||
return (
|
handleSearch();
|
||||||
<Typography.Link
|
|
||||||
style={style}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedStaff(record);
|
|
||||||
setViewMode("log");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{n}
|
|
||||||
</Typography.Link>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
}}
|
||||||
title: "就职状态",
|
|
||||||
dataIndex: "employmentStatusName",
|
|
||||||
width: 90,
|
|
||||||
render: (_, record) => getEmploymentStatusLabel(record),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "离职申请审核状态",
|
|
||||||
dataIndex: "resignAuditStatus",
|
|
||||||
width: 140,
|
|
||||||
render: (_, record) => getResignAuditStatusLabel(record),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "操作",
|
|
||||||
width: 220,
|
|
||||||
fixed: "right",
|
|
||||||
render: (_, record) => (
|
|
||||||
<Space>
|
|
||||||
<Button type="link" style={{ padding: 0 }} onClick={() => openResignView(record)}>查看</Button>
|
|
||||||
{record.resignApplyId && Number(record.resignAuditStatus) === 0 && (
|
|
||||||
<Button type="link" style={{ padding: 0 }} onClick={() => openResignAudit(record)}>离职审核</Button>
|
|
||||||
)}
|
|
||||||
<Button danger type="link" style={{ padding: 0 }} onClick={() => onDelete(record)}>删除</Button>
|
|
||||||
</Space>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
{...tableProps}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|
@ -231,20 +306,20 @@ function PersonnelChangePage(props) {
|
||||||
destroyOnHidden
|
destroyOnHidden
|
||||||
title="审核离职申请"
|
title="审核离职申请"
|
||||||
width={720}
|
width={720}
|
||||||
footer={(
|
footer={
|
||||||
<Space>
|
<Space>
|
||||||
<Button onClick={() => setAuditModalOpen(false)}>取消</Button>
|
<Button onClick={() => setAuditModalOpen(false)}>取消</Button>
|
||||||
<Button danger onClick={() => submitAudit(false)}>退回</Button>
|
<Button danger onClick={() => submitAudit(false)}>退回</Button>
|
||||||
<Button type="primary" onClick={() => submitAudit(true)}>通过</Button>
|
<Button type="primary" onClick={() => submitAudit(true)}>通过</Button>
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
}
|
||||||
onCancel={() => setAuditModalOpen(false)}
|
onCancel={() => setAuditModalOpen(false)}
|
||||||
>
|
>
|
||||||
<Descriptions bordered column={1} labelStyle={{ width: 120 }}>
|
<Descriptions bordered column={1} labelStyle={{ width: 120 }}>
|
||||||
<Descriptions.Item label="申请人">{resignInfo.applicantName}</Descriptions.Item>
|
<Descriptions.Item label="申请人">{resignInfo.applicantName}</Descriptions.Item>
|
||||||
<Descriptions.Item label="申请时间">{resignInfo.applyTime}</Descriptions.Item>
|
<Descriptions.Item label="申请时间">{resignInfo.applyTime}</Descriptions.Item>
|
||||||
<Descriptions.Item label="预计离职日期">{resignInfo.expectedLeaveDate}</Descriptions.Item>
|
<Descriptions.Item label="预计离职日期">{resignInfo.expectedResignDate}</Descriptions.Item>
|
||||||
<Descriptions.Item label="离职原因">{resignInfo.leaveReason}</Descriptions.Item>
|
<Descriptions.Item label="离职原因">{resignInfo.resignReason}</Descriptions.Item>
|
||||||
<Descriptions.Item label="备注">{resignInfo.remark}</Descriptions.Item>
|
<Descriptions.Item label="备注">{resignInfo.remark}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
<Form.Item label="退回原因" style={{ marginTop: 16 }}>
|
<Form.Item label="退回原因" style={{ marginTop: 16 }}>
|
||||||
|
|
@ -264,8 +339,8 @@ function PersonnelChangePage(props) {
|
||||||
<Descriptions bordered column={1} labelStyle={{ width: 120 }}>
|
<Descriptions bordered column={1} labelStyle={{ width: 120 }}>
|
||||||
<Descriptions.Item label="申请人">{resignInfo.applicantName}</Descriptions.Item>
|
<Descriptions.Item label="申请人">{resignInfo.applicantName}</Descriptions.Item>
|
||||||
<Descriptions.Item label="申请时间">{resignInfo.applyTime}</Descriptions.Item>
|
<Descriptions.Item label="申请时间">{resignInfo.applyTime}</Descriptions.Item>
|
||||||
<Descriptions.Item label="预计离职日期">{resignInfo.expectedLeaveDate}</Descriptions.Item>
|
<Descriptions.Item label="预计离职日期">{resignInfo.expectedResignDate}</Descriptions.Item>
|
||||||
<Descriptions.Item label="离职原因">{resignInfo.leaveReason}</Descriptions.Item>
|
<Descriptions.Item label="离职原因">{resignInfo.resignReason}</Descriptions.Item>
|
||||||
<Descriptions.Item label="备注">{resignInfo.remark}</Descriptions.Item>
|
<Descriptions.Item label="备注">{resignInfo.remark}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
@ -273,4 +348,4 @@ function PersonnelChangePage(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Connect([NS_STAFF_CHANGE_LOG, NS_STAFF_INFO], true)(PersonnelChangePage);
|
export default Connect([NS_STAFF_CHANGE_LOG, NS_STAFF_INFO], true)(AntdTableFuncControl(PersonnelChangePage));
|
||||||
|
|
@ -1,45 +1,42 @@
|
||||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import { Button, Descriptions, Form, message, Modal, Space } from "antd";
|
import { Button, DatePicker, Descriptions, Form, Input, message, Modal, Select, Table, Upload } from "antd";
|
||||||
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FormBuilder from "zy-react-library/components/FormBuilder";
|
|
||||||
import AddIcon from "zy-react-library/components/Icon/AddIcon";
|
|
||||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||||
import CertPreviewImg from "~/components/CertPreviewImg";
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
||||||
import Search from "zy-react-library/components/Search";
|
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
|
||||||
import Table from "zy-react-library/components/Table";
|
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||||
import Upload from "zy-react-library/components/Upload";
|
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
||||||
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
|
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||||
import useGetFile from "zy-react-library/hooks/useGetFile";
|
|
||||||
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
|
|
||||||
import useTable from "zy-react-library/hooks/useTable";
|
|
||||||
import { NS_STAFF_CERTIFICATE } from "~/enumerate/namespace";
|
import { NS_STAFF_CERTIFICATE } from "~/enumerate/namespace";
|
||||||
import { safeGetFiles, safeListRequest, safeRequest } from "~/utils";
|
import CertPreviewImg from "~/components/CertPreviewImg";
|
||||||
import { mockUploadFileList, resolveUploadFileId } from "~/utils/mockUpload";
|
|
||||||
import { dateRangeRule } from "~/utils/validators";
|
import { dateRangeRule } from "~/utils/validators";
|
||||||
|
|
||||||
|
const { router } = tools;
|
||||||
|
|
||||||
function StaffCertificatePage(props) {
|
function StaffCertificatePage(props) {
|
||||||
const query = useGetUrlQuery();
|
const urlParams = Object.fromEntries(new URLSearchParams(window.location.search));
|
||||||
const staffId = query.staffId;
|
const staffId = urlParams.staffId;
|
||||||
const staffName = query.staffName;
|
const staffName = urlParams.staffName ? decodeURIComponent(urlParams.staffName) : "";
|
||||||
const [addModalOpen, setAddModalOpen] = useState(false);
|
const [addModalOpen, setAddModalOpen] = useState(false);
|
||||||
const [viewModalOpen, setViewModalOpen] = useState(false);
|
const [viewModalOpen, setViewModalOpen] = useState(false);
|
||||||
const [currentId, setCurrentId] = useState("");
|
const [currentId, setCurrentId] = useState("");
|
||||||
const [form] = Form.useForm();
|
const [searchForm] = Form.useForm();
|
||||||
|
const { staffCertificate } = props;
|
||||||
const { tableProps, getData } = useTable(safeListRequest(props.staffCertificateList), {
|
const {
|
||||||
form,
|
staffCertificateList: dataSource,
|
||||||
transform: (formData) => ({
|
staffCertificateTotal: total,
|
||||||
...formData,
|
staffCertificateLoading: loading,
|
||||||
eqStaffId: staffId,
|
} = staffCertificate || {};
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (staffId) {
|
searchForm.setFieldsValue(router.query);
|
||||||
getData();
|
handleSearch();
|
||||||
}
|
}, []);
|
||||||
}, [staffId]);
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
props.staffCertificateList({ ...router.query, personnelId: staffId });
|
||||||
|
};
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
window.location.href = window.location.pathname.replace(/\/Certificate.*$/, "/List");
|
window.location.href = window.location.pathname.replace(/\/Certificate.*$/, "/List");
|
||||||
|
|
@ -55,7 +52,7 @@ function StaffCertificatePage(props) {
|
||||||
const res = await props.staffCertificateRemove({ id });
|
const res = await props.staffCertificateRemove({ id });
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
message.success("删除成功");
|
message.success("删除成功");
|
||||||
getData();
|
handleSearch();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -64,28 +61,32 @@ function StaffCertificatePage(props) {
|
||||||
return (
|
return (
|
||||||
<PageLayout title={`人员证书 - ${staffName || ""}`}>
|
<PageLayout title={`人员证书 - ${staffName || ""}`}>
|
||||||
<Button style={{ marginBottom: 16 }} onClick={goBack}>返回</Button>
|
<Button style={{ marginBottom: 16 }} onClick={goBack}>返回</Button>
|
||||||
<Search
|
<SearchForm
|
||||||
form={form}
|
style={{ marginBottom: 24 }}
|
||||||
options={[
|
form={searchForm}
|
||||||
{ name: "likeCertNo", label: "证书编号", placeholder: "请输入证书编号" },
|
loading={loading}
|
||||||
{ name: "certCategory", label: "证书类别" },
|
formLine={[
|
||||||
{ name: "certWorkCategory", label: "证书作业类别" },
|
<Form.Item key="certNo" name="certNo">
|
||||||
|
<ControlWrapper.Input label="证书编号" placeholder="请输入证书编号" allowClear />
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="certCategory" name="certCategory">
|
||||||
|
<ControlWrapper.Input label="证书类别" placeholder="请输入证书类别" allowClear />
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="certWorkCategory" name="certWorkCategory">
|
||||||
|
<ControlWrapper.Input label="证书作业类别" placeholder="请输入证书作业类别" allowClear />
|
||||||
|
</Form.Item>,
|
||||||
]}
|
]}
|
||||||
onFinish={getData}
|
onReset={() => {
|
||||||
|
router.query = { ...router.query, current: 1, size: 10 };
|
||||||
|
handleSearch();
|
||||||
|
}}
|
||||||
|
onFinish={() => {
|
||||||
|
router.query = { ...router.query, current: 1, size: 10 };
|
||||||
|
handleSearch();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Table
|
<Table
|
||||||
toolBarRender={() => (
|
rowKey="id"
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
icon={<AddIcon />}
|
|
||||||
onClick={() => {
|
|
||||||
setCurrentId("");
|
|
||||||
setAddModalOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
添加证书
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
columns={[
|
columns={[
|
||||||
{ title: "证书类型", dataIndex: "certCategory" },
|
{ title: "证书类型", dataIndex: "certCategory" },
|
||||||
{ title: "证书作业类别", dataIndex: "certWorkCategory" },
|
{ title: "证书作业类别", dataIndex: "certWorkCategory" },
|
||||||
|
|
@ -94,19 +95,33 @@ function StaffCertificatePage(props) {
|
||||||
title: "操作",
|
title: "操作",
|
||||||
width: 200,
|
width: 200,
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space>
|
<TableAction>
|
||||||
<Button type="link" onClick={() => { setCurrentId(record.id); setViewModalOpen(true); }}>
|
<Button type="link" size="small" onClick={() => { setCurrentId(record.id); setViewModalOpen(true); }}>
|
||||||
查看
|
查看
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="link" onClick={() => { setCurrentId(record.id); setAddModalOpen(true); }}>
|
<Button type="link" size="small" onClick={() => { setCurrentId(record.id); setAddModalOpen(true); }}>
|
||||||
编辑
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
<Button danger type="link" onClick={() => onDelete(record.id)}>删除</Button>
|
<Button danger type="link" size="small" onClick={() => onDelete(record.id)}>删除</Button>
|
||||||
</Space>
|
</TableAction>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
{...tableProps}
|
dataSource={dataSource}
|
||||||
|
scroll={{ y: props.scrollY }}
|
||||||
|
loading={loading}
|
||||||
|
pagination={{
|
||||||
|
total,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (t) => `共 ${t} 条`,
|
||||||
|
current: Number(router.query.current) || 1,
|
||||||
|
pageSize: Number(router.query.size) || 10,
|
||||||
|
onChange: (page, pageSize) => {
|
||||||
|
router.query = { ...router.query, current: page, size: pageSize };
|
||||||
|
handleSearch();
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{addModalOpen && (
|
{addModalOpen && (
|
||||||
|
|
@ -114,21 +129,21 @@ function StaffCertificatePage(props) {
|
||||||
open={addModalOpen}
|
open={addModalOpen}
|
||||||
staffId={staffId}
|
staffId={staffId}
|
||||||
currentId={currentId}
|
currentId={currentId}
|
||||||
requestAdd={props.staffCertificateAdd}
|
staffCertificateAdd={props.staffCertificateAdd}
|
||||||
requestEdit={props.staffCertificateEdit}
|
staffCertificateEdit={props.staffCertificateEdit}
|
||||||
requestDetails={props.staffCertificateInfo}
|
staffCertificateInfo={props.staffCertificateInfo}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setAddModalOpen(false);
|
setAddModalOpen(false);
|
||||||
setCurrentId("");
|
setCurrentId("");
|
||||||
}}
|
}}
|
||||||
onSuccess={getData}
|
onSuccess={handleSearch}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{viewModalOpen && (
|
{viewModalOpen && (
|
||||||
<ViewModal
|
<ViewModal
|
||||||
open={viewModalOpen}
|
open={viewModalOpen}
|
||||||
currentId={currentId}
|
currentId={currentId}
|
||||||
requestDetails={props.staffCertificateInfo}
|
staffCertificateInfo={props.staffCertificateInfo}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setViewModalOpen(false);
|
setViewModalOpen(false);
|
||||||
setCurrentId("");
|
setCurrentId("");
|
||||||
|
|
@ -140,83 +155,91 @@ function StaffCertificatePage(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function CertModal({
|
function CertModal({
|
||||||
open, staffId, currentId, requestAdd, requestEdit, requestDetails, onCancel, onSuccess,
|
open, staffId, currentId, staffCertificateAdd, staffCertificateEdit, staffCertificateInfo, onCancel, onSuccess,
|
||||||
}) {
|
}) {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [detailLoading, setDetailLoading] = useState(false);
|
const [detailLoading, setDetailLoading] = useState(false);
|
||||||
const { getFile } = useGetFile();
|
const [uploadFileList, setUploadFileList] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) {
|
if (!open) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!currentId) {
|
if (!currentId) {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
form.setFieldsValue({ certImgs: mockUploadFileList("证书附件.jpg") });
|
setUploadFileList([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
(async () => {
|
(async () => {
|
||||||
setDetailLoading(true);
|
setDetailLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await safeRequest(requestDetails, { id: currentId });
|
const res = await staffCertificateInfo({ id: currentId });
|
||||||
if (cancelled || !res?.data) {
|
if (cancelled || !res?.data) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data = { ...res.data };
|
const data = { ...res.data };
|
||||||
data.validDate = [data.validStartDate, data.validEndDate];
|
// 将 validStartDate/validEndDate 合并为 validDate(RangePicker)
|
||||||
data.certImgs = data.certImgFiles?.length ? data.certImgFiles : mockUploadFileList("证书附件.jpg");
|
if (data.validStartDate || data.validEndDate) {
|
||||||
|
data.validDate = [data.validStartDate, data.validEndDate];
|
||||||
|
}
|
||||||
|
// 处理证书附件 fileList
|
||||||
|
let fileList = [];
|
||||||
|
if (Array.isArray(data.certImgFiles)) {
|
||||||
|
fileList = data.certImgFiles.map((f, i) => ({
|
||||||
|
uid: f.uid || `-${i}`,
|
||||||
|
name: f.fileName || f.name || `证书附件${i + 1}`,
|
||||||
|
status: "done",
|
||||||
|
url: f.url,
|
||||||
|
}));
|
||||||
|
} else if (data.certAttachmentUrl) {
|
||||||
|
fileList = data.certAttachmentUrl.split(",").filter(Boolean).map((url, i) => ({
|
||||||
|
uid: `-${i}`,
|
||||||
|
name: url.split("/").pop() || `证书附件${i + 1}`,
|
||||||
|
status: "done",
|
||||||
|
url,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
data.certAttachmentUrl = fileList;
|
||||||
|
setUploadFileList(fileList);
|
||||||
form.setFieldsValue(data);
|
form.setFieldsValue(data);
|
||||||
setDetailLoading(false);
|
} catch (err) {
|
||||||
|
|
||||||
const files = await safeGetFiles(
|
|
||||||
getFile,
|
|
||||||
{ eqType: "staff_cert", eqForeignKey: data.id },
|
|
||||||
data.certImgFiles || [],
|
|
||||||
);
|
|
||||||
if (!cancelled && files.length) {
|
|
||||||
form.setFieldValue("certImgs", files);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.warn("[StaffCertificate] load detail failed:", err);
|
console.warn("[StaffCertificate] load detail failed:", err);
|
||||||
}
|
} finally {
|
||||||
finally {
|
if (!cancelled) setDetailLoading(false);
|
||||||
if (!cancelled) {
|
|
||||||
setDetailLoading(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
return () => {
|
return () => { cancelled = true; };
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
}, [open, currentId]);
|
}, [open, currentId]);
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
|
setUploadFileList([]);
|
||||||
onCancel();
|
onCancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async (values) => {
|
const handleSubmit = async (values) => {
|
||||||
try {
|
try {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
values.validStartDate = values.validDate?.[0];
|
const payload = {
|
||||||
values.validEndDate = values.validDate?.[1];
|
...values,
|
||||||
values.staffId = staffId;
|
personnelId: staffId,
|
||||||
values.certAttachmentUrl = resolveUploadFileId(values.certImgs);
|
validStartDate: values.validDate?.[0],
|
||||||
const request = currentId ? requestEdit : requestAdd;
|
validEndDate: values.validDate?.[1],
|
||||||
if (currentId) {
|
certAttachmentUrl: Array.isArray(values.certAttachmentUrl)
|
||||||
values.id = currentId;
|
? values.certAttachmentUrl.map((f) => f.url || f.response?.url).filter(Boolean).join(",")
|
||||||
}
|
: "",
|
||||||
const res = await request(values);
|
};
|
||||||
|
delete payload.validDate;
|
||||||
|
delete payload.certImgFiles;
|
||||||
|
if (currentId) payload.id = currentId;
|
||||||
|
const request = currentId ? staffCertificateEdit : staffCertificateAdd;
|
||||||
|
const res = await request(payload);
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
message.success(currentId ? "编辑成功" : "添加成功");
|
message.success(currentId ? "编辑成功" : "添加成功");
|
||||||
handleCancel();
|
handleCancel();
|
||||||
onSuccess();
|
onSuccess();
|
||||||
|
} else {
|
||||||
|
message.error(res?.message || "操作失败");
|
||||||
}
|
}
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -232,46 +255,60 @@ function CertModal({
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
onOk={form.submit}
|
onOk={form.submit}
|
||||||
>
|
>
|
||||||
<FormBuilder
|
<Form form={form} layout="vertical" onFinish={handleSubmit}>
|
||||||
form={form}
|
<Form.Item name="certName" label="证照名称" rules={[{ required: true, message: "请输入证照名称" }]}>
|
||||||
span={24}
|
<Input placeholder="请输入证照名称" />
|
||||||
showActionButtons={false}
|
</Form.Item>
|
||||||
onFinish={handleSubmit}
|
<Form.Item name="certCategory" label="证书类别" rules={[{ required: true, message: "请选择证书类别" }]}>
|
||||||
options={[
|
<Input placeholder="请输入证书类别" />
|
||||||
{ name: "certName", label: "证照名称", rules: [{ required: true, message: "请输入证照名称" }] },
|
</Form.Item>
|
||||||
{ name: "certCategory", label: "证书类别", rules: [{ required: true, message: "请选择证书类别" }] },
|
<Form.Item name="certWorkCategory" label="证书作业类别">
|
||||||
{ name: "certWorkCategory", label: "证书作业类别" },
|
<Input placeholder="请输入证书作业类别" />
|
||||||
{ name: "certNo", label: "证书编号", rules: [{ required: true, message: "请输入证书编号" }] },
|
</Form.Item>
|
||||||
{ name: "issueOrg", label: "发证机关", rules: [{ required: true, message: "请输入发证机关" }] },
|
<Form.Item name="certNo" label="证书编号" rules={[{ required: true, message: "请输入证书编号" }]}>
|
||||||
{
|
<Input placeholder="请输入证书编号" />
|
||||||
name: "validDate",
|
</Form.Item>
|
||||||
label: "证书有效开始/结束日期",
|
<Form.Item name="issueOrg" label="发证机关" rules={[{ required: true, message: "请输入发证机关" }]}>
|
||||||
render: FORM_ITEM_RENDER_ENUM.DATE_RANGE,
|
<Input placeholder="请输入发证机关" />
|
||||||
rules: [{ required: true, message: "请选择证书有效期" }, dateRangeRule()],
|
</Form.Item>
|
||||||
},
|
<Form.Item
|
||||||
{ name: "reviewDate", label: "复合日期", render: FORM_ITEM_RENDER_ENUM.DATE },
|
name="validDate"
|
||||||
{
|
label="证书有效开始/结束日期"
|
||||||
name: "certImgs",
|
rules={[{ required: true, message: "请选择证书有效期" }, dateRangeRule()]}
|
||||||
label: "证书附件",
|
>
|
||||||
render: (
|
<DatePicker.RangePicker style={{ width: "100%" }} />
|
||||||
<Upload
|
</Form.Item>
|
||||||
maxCount={3}
|
<Form.Item name="reviewDate" label="复合日期">
|
||||||
onGetRemoveFile={(file) => setDeleteFiles(prev => [...prev, file])}
|
<DatePicker style={{ width: "100%" }} />
|
||||||
/>
|
</Form.Item>
|
||||||
),
|
<Form.Item
|
||||||
},
|
name="certAttachmentUrl"
|
||||||
]}
|
label="证书附件"
|
||||||
labelCol={{ span: 24 }}
|
valuePropName="fileList"
|
||||||
wrapperCol={{ span: 24 }}
|
getValueFromEvent={(e) => {
|
||||||
/>
|
if (Array.isArray(e)) return e;
|
||||||
|
return e?.fileList;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Upload
|
||||||
|
action="/api/upload"
|
||||||
|
maxCount={3}
|
||||||
|
fileList={uploadFileList}
|
||||||
|
onChange={(info) => {
|
||||||
|
setUploadFileList(info.fileList);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button icon={<UploadOutlined />}>上传文件</Button>
|
||||||
|
</Upload>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewModal({ open, currentId, requestDetails, onCancel }) {
|
function ViewModal({ open, currentId, staffCertificateInfo, onCancel }) {
|
||||||
const [info, setInfo] = useState({});
|
const [info, setInfo] = useState({});
|
||||||
const [detailLoading, setDetailLoading] = useState(false);
|
const [detailLoading, setDetailLoading] = useState(false);
|
||||||
const { getFile } = useGetFile();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open || !currentId) {
|
if (!open || !currentId) {
|
||||||
|
|
@ -282,36 +319,29 @@ function ViewModal({ open, currentId, requestDetails, onCancel }) {
|
||||||
(async () => {
|
(async () => {
|
||||||
setDetailLoading(true);
|
setDetailLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await safeRequest(requestDetails, { id: currentId });
|
const res = await staffCertificateInfo({ id: currentId });
|
||||||
if (cancelled || !res?.data) {
|
if (cancelled || !res?.data) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data = { ...res.data };
|
const data = { ...res.data };
|
||||||
data.certImgs = data.certImgFiles || [];
|
// 处理 certImgFiles
|
||||||
|
if (Array.isArray(data.certImgFiles)) {
|
||||||
|
data.certDisplayFiles = data.certImgFiles;
|
||||||
|
} else if (data.certAttachmentUrl) {
|
||||||
|
data.certDisplayFiles = data.certAttachmentUrl.split(",").filter(Boolean).map((url, i) => ({
|
||||||
|
uid: `-${i}`,
|
||||||
|
fileName: url.split("/").pop() || `证书附件${i + 1}`,
|
||||||
|
url,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
data.certDisplayFiles = [];
|
||||||
|
}
|
||||||
setInfo(data);
|
setInfo(data);
|
||||||
setDetailLoading(false);
|
} catch (err) {
|
||||||
|
|
||||||
const files = await safeGetFiles(
|
|
||||||
getFile,
|
|
||||||
{ eqType: "staff_cert", eqForeignKey: data.id },
|
|
||||||
data.certImgFiles || [],
|
|
||||||
);
|
|
||||||
if (!cancelled && files.length) {
|
|
||||||
setInfo((prev) => ({ ...prev, certImgs: files }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.warn("[StaffCertificate] load view failed:", err);
|
console.warn("[StaffCertificate] load view failed:", err);
|
||||||
}
|
} finally {
|
||||||
finally {
|
if (!cancelled) setDetailLoading(false);
|
||||||
if (!cancelled) {
|
|
||||||
setDetailLoading(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
return () => {
|
return () => { cancelled = true; };
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
}, [open, currentId]);
|
}, [open, currentId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -333,10 +363,10 @@ function ViewModal({ open, currentId, requestDetails, onCancel }) {
|
||||||
<Descriptions.Item label="证书有效开始日期">{info.validStartDate}</Descriptions.Item>
|
<Descriptions.Item label="证书有效开始日期">{info.validStartDate}</Descriptions.Item>
|
||||||
<Descriptions.Item label="证书有效结束日期">{info.validEndDate}</Descriptions.Item>
|
<Descriptions.Item label="证书有效结束日期">{info.validEndDate}</Descriptions.Item>
|
||||||
<Descriptions.Item label="复合日期">{info.reviewDate}</Descriptions.Item>
|
<Descriptions.Item label="复合日期">{info.reviewDate}</Descriptions.Item>
|
||||||
<Descriptions.Item label="证书图片"><CertPreviewImg files={info.certImgs} /></Descriptions.Item>
|
<Descriptions.Item label="证书图片"><CertPreviewImg files={info.certDisplayFiles} /></Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Connect([NS_STAFF_CERTIFICATE], true)(StaffCertificatePage);
|
export default Connect([NS_STAFF_CERTIFICATE], true)(AntdTableFuncControl(StaffCertificatePage));
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import { Button, DatePicker, Form, Input, message, Modal, Row, Col, Select, Space } from "antd";
|
import { Button, DatePicker, Form, Input, message, Modal, Row, Col, Select, Table, Upload } from "antd";
|
||||||
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import AddIcon from "zy-react-library/components/Icon/AddIcon";
|
import dayjs from "dayjs";
|
||||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||||
import Search from "zy-react-library/components/Search";
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
||||||
import Table from "zy-react-library/components/Table";
|
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
|
||||||
import Upload from "zy-react-library/components/Upload";
|
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||||
import useTable from "zy-react-library/hooks/useTable";
|
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
||||||
import { NS_ORG_DEPARTMENT, NS_ORG_POSITION, NS_STAFF_INFO } from "~/enumerate/namespace";
|
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||||
|
import { Get } from "@cqsjjb/jjb-common-lib/http";
|
||||||
|
import { NS_STAFF_INFO } from "~/enumerate/namespace";
|
||||||
import {
|
import {
|
||||||
EDUCATION_LEVEL_OPTIONS,
|
EDUCATION_LEVEL_OPTIONS,
|
||||||
EDUCATION_TYPE_OPTIONS,
|
EDUCATION_TYPE_OPTIONS,
|
||||||
|
|
@ -16,21 +19,11 @@ import {
|
||||||
QUALIFICATION_INDUSTRY_OPTIONS,
|
QUALIFICATION_INDUSTRY_OPTIONS,
|
||||||
REGISTER_ENGINEER_OPTIONS,
|
REGISTER_ENGINEER_OPTIONS,
|
||||||
} from "~/enumerate/enterpriseOptions";
|
} from "~/enumerate/enterpriseOptions";
|
||||||
import { ensureOrgContext, fetchOrgDepartmentPage, fetchOrgPositionPage } from "~/api/enterpriseInfo/orgBootstrap";
|
import { getBirthDateFromIdCard } from "~/utils";
|
||||||
import { asId, sameId } from "~/api/enterpriseInfo/idUtil";
|
|
||||||
import { getBirthDateFromIdCard, safeListRequest, safeRequest } from "~/utils";
|
|
||||||
import { toDayjs } from "~/utils/dateFormat";
|
|
||||||
import { formSelectField } from "~/utils/enterpriseForm";
|
|
||||||
import { idCardRule, mobileRule } from "~/utils/validators";
|
import { idCardRule, mobileRule } from "~/utils/validators";
|
||||||
import StaffViewModal from "../StaffViewModal";
|
import StaffViewModal from "../StaffViewModal";
|
||||||
|
|
||||||
function mapPositionOptions(list = []) {
|
const { router } = tools;
|
||||||
return list.map((p) => ({
|
|
||||||
label: p.positionName,
|
|
||||||
value: asId(p.id),
|
|
||||||
deptId: asId(p.deptId),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function PersonnelInfoPage(props) {
|
function PersonnelInfoPage(props) {
|
||||||
const [formModalOpen, setFormModalOpen] = useState(false);
|
const [formModalOpen, setFormModalOpen] = useState(false);
|
||||||
|
|
@ -39,43 +32,47 @@ function PersonnelInfoPage(props) {
|
||||||
const [searchForm] = Form.useForm();
|
const [searchForm] = Form.useForm();
|
||||||
const [deptOptions, setDeptOptions] = useState([]);
|
const [deptOptions, setDeptOptions] = useState([]);
|
||||||
const [positionOptions, setPositionOptions] = useState([]);
|
const [positionOptions, setPositionOptions] = useState([]);
|
||||||
|
const { staffInfo } = props;
|
||||||
|
const {
|
||||||
|
staffInfoList: dataSource,
|
||||||
|
staffInfoTotal: total,
|
||||||
|
staffInfoLoading: loading,
|
||||||
|
} = staffInfo || {};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
await ensureOrgContext();
|
|
||||||
const [deptRes, posRes] = await Promise.all([
|
const [deptRes, posRes] = await Promise.all([
|
||||||
fetchOrgDepartmentPage({ pageIndex: 1, pageSize: 200 }),
|
Get("/safetyEval/org-department/page", { current: 1, size: 200 }),
|
||||||
fetchOrgPositionPage({ pageIndex: 1, pageSize: 200 }),
|
Get("/safetyEval/org-position/page", { current: 1, size: 200 }),
|
||||||
]);
|
]);
|
||||||
if (cancelled) {
|
if (cancelled) return;
|
||||||
return;
|
setDeptOptions((deptRes?.data || []).map((d) => ({ label: d.deptName, value: d.id })));
|
||||||
}
|
setPositionOptions((posRes?.data || []).map((p) => ({
|
||||||
setDeptOptions((deptRes?.data || []).map((d) => ({ label: d.deptName, value: asId(d.id) })));
|
label: p.positionName,
|
||||||
setPositionOptions(mapPositionOptions(posRes?.data));
|
value: p.id,
|
||||||
}
|
deptId: p.deptId,
|
||||||
catch (err) {
|
})));
|
||||||
|
} catch (err) {
|
||||||
console.warn("[PersonnelInfo] load dept/position options failed:", err);
|
console.warn("[PersonnelInfo] load dept/position options failed:", err);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
return () => {
|
return () => { cancelled = true; };
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { tableProps, getData } = useTable(safeListRequest(props.staffInfoList), {
|
useEffect(() => {
|
||||||
form: searchForm,
|
searchForm.setFieldsValue(router.query);
|
||||||
transform: (formData) => ({
|
handleSearch();
|
||||||
...formData,
|
}, []);
|
||||||
eqDeptId: formData.deptId,
|
|
||||||
eqPositionId: formData.positionId,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const goCertificate = (staffId, staffName) => {
|
const handleSearch = () => {
|
||||||
|
props.staffInfoList({ ...router.query });
|
||||||
|
};
|
||||||
|
|
||||||
|
const goCertificate = (id, staffName) => {
|
||||||
const base = window.location.pathname.replace(/\/List.*$/, "");
|
const base = window.location.pathname.replace(/\/List.*$/, "");
|
||||||
window.location.href = `${base}/Certificate?staffId=${staffId}&staffName=${encodeURIComponent(staffName || "")}`;
|
window.location.href = `${base}/Certificate?staffId=${id}&staffName=${encodeURIComponent(staffName || "")}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDelete = (id) => {
|
const onDelete = (id) => {
|
||||||
|
|
@ -88,7 +85,7 @@ function PersonnelInfoPage(props) {
|
||||||
const res = await props.staffInfoRemove({ id });
|
const res = await props.staffInfoRemove({ id });
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
message.success("删除成功");
|
message.success("删除成功");
|
||||||
getData();
|
handleSearch();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -120,30 +117,42 @@ function PersonnelInfoPage(props) {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Search
|
<SearchForm
|
||||||
|
style={{ marginBottom: 24 }}
|
||||||
form={searchForm}
|
form={searchForm}
|
||||||
options={[
|
loading={loading}
|
||||||
{ name: "likeStaffName", label: "用户名称", placeholder: "请输入用户名称" },
|
formLine={[
|
||||||
formSelectField("deptId", "部门", deptOptions),
|
<Form.Item key="userName" name="userName">
|
||||||
formSelectField("positionId", "岗位", positionOptions),
|
<ControlWrapper.Input label="用户名称" placeholder="请输入用户名称" allowClear />
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="deptId" name="deptId">
|
||||||
|
<ControlWrapper.Select label="部门" placeholder="请选择部门" allowClear style={{ width: "100%" }}>
|
||||||
|
{deptOptions.map((d) => (
|
||||||
|
<Select.Option key={d.value} value={d.value}>{d.label}</Select.Option>
|
||||||
|
))}
|
||||||
|
</ControlWrapper.Select>
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="postId" name="postId">
|
||||||
|
<ControlWrapper.Select label="岗位" placeholder="请选择岗位" allowClear style={{ width: "100%" }}>
|
||||||
|
{positionOptions.map((p) => (
|
||||||
|
<Select.Option key={p.value} value={p.value}>{p.label}</Select.Option>
|
||||||
|
))}
|
||||||
|
</ControlWrapper.Select>
|
||||||
|
</Form.Item>,
|
||||||
]}
|
]}
|
||||||
onFinish={getData}
|
onReset={() => {
|
||||||
|
router.query = { ...router.query, current: 1, size: 10 };
|
||||||
|
handleSearch();
|
||||||
|
}}
|
||||||
|
onFinish={() => {
|
||||||
|
router.query = { ...router.query, current: 1, size: 10 };
|
||||||
|
handleSearch();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Table
|
<Table
|
||||||
toolBarRender={() => (
|
rowKey="id"
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
icon={<AddIcon />}
|
|
||||||
onClick={() => {
|
|
||||||
setCurrentId("");
|
|
||||||
setFormModalOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
新增人员
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
columns={[
|
columns={[
|
||||||
{ title: "用户名称", dataIndex: "staffName" },
|
{ title: "用户名称", dataIndex: "userName" },
|
||||||
{ title: "账号", dataIndex: "account" },
|
{ title: "账号", dataIndex: "account" },
|
||||||
{ title: "部门", dataIndex: "deptName" },
|
{ title: "部门", dataIndex: "deptName" },
|
||||||
{ title: "岗位", dataIndex: "positionName" },
|
{ title: "岗位", dataIndex: "positionName" },
|
||||||
|
|
@ -157,38 +166,53 @@ function PersonnelInfoPage(props) {
|
||||||
title: "操作",
|
title: "操作",
|
||||||
width: 320,
|
width: 320,
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space wrap>
|
<TableAction>
|
||||||
<Button type="link" onClick={() => { setCurrentId(asId(record.id)); setViewModalOpen(true); }}>
|
<Button type="link" size="small" onClick={() => { setCurrentId(record.id); setViewModalOpen(true); }}>
|
||||||
查看
|
查看
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="link" onClick={() => { setCurrentId(asId(record.id)); setFormModalOpen(true); }}>
|
<Button type="link" size="small" onClick={() => { setCurrentId(record.id); setFormModalOpen(true); }}>
|
||||||
编辑
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="link" onClick={() => goCertificate(asId(record.id), record.staffName)}>
|
<Button type="link" size="small" onClick={() => { setCurrentId(record.id); goCertificate(record.id, record.userName); }}>
|
||||||
证书
|
证书
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="link" onClick={() => onResetPassword(asId(record.id))}>重置密码</Button>
|
<Button type="link" size="small" onClick={() => onResetPassword(record.id)}>重置密码</Button>
|
||||||
<Button danger type="link" onClick={() => onDelete(asId(record.id))}>删除</Button>
|
<Button danger type="link" size="small" onClick={() => onDelete(record.id)}>删除</Button>
|
||||||
</Space>
|
</TableAction>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
{...tableProps}
|
dataSource={dataSource}
|
||||||
|
scroll={{ y: props.scrollY }}
|
||||||
|
loading={loading}
|
||||||
|
pagination={{
|
||||||
|
total,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (t) => `共 ${t} 条`,
|
||||||
|
current: Number(router.query.current) || 1,
|
||||||
|
pageSize: Number(router.query.size) || 10,
|
||||||
|
onChange: (page, pageSize) => {
|
||||||
|
router.query = { ...router.query, current: page, size: pageSize };
|
||||||
|
handleSearch();
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{formModalOpen && (
|
{formModalOpen && (
|
||||||
<StaffFormModal
|
<StaffFormModal
|
||||||
open={formModalOpen}
|
open={formModalOpen}
|
||||||
currentId={currentId}
|
currentId={currentId}
|
||||||
requestAdd={props.staffInfoAdd}
|
staffInfoGet={props.staffInfoGet}
|
||||||
requestEdit={props.staffInfoEdit}
|
staffInfoAdd={props.staffInfoAdd}
|
||||||
requestDetails={props.staffInfoGet}
|
staffInfoEdit={props.staffInfoEdit}
|
||||||
deptOptions={deptOptions}
|
deptOptions={deptOptions}
|
||||||
|
positionOptions={positionOptions}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setFormModalOpen(false);
|
setFormModalOpen(false);
|
||||||
setCurrentId("");
|
setCurrentId("");
|
||||||
}}
|
}}
|
||||||
onSuccess={getData}
|
onSuccess={handleSearch}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{viewModalOpen && (
|
{viewModalOpen && (
|
||||||
|
|
@ -207,60 +231,41 @@ function PersonnelInfoPage(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function StaffFormModal({
|
function StaffFormModal({
|
||||||
open, currentId, requestAdd, requestEdit, requestDetails, deptOptions, onCancel, onSuccess,
|
open, currentId, staffInfoGet, staffInfoAdd, staffInfoEdit, deptOptions, positionOptions, onCancel, onSuccess,
|
||||||
}) {
|
}) {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [detailLoading, setDetailLoading] = useState(false);
|
const [detailLoading, setDetailLoading] = useState(false);
|
||||||
const [deptPositionOptions, setDeptPositionOptions] = useState([]);
|
const [deptPositionOptions, setDeptPositionOptions] = useState([]);
|
||||||
const [positionLoading, setPositionLoading] = useState(false);
|
const [positionLoading, setPositionLoading] = useState(false);
|
||||||
|
const [uploadFileList, setUploadFileList] = useState([]);
|
||||||
const watchedDeptId = Form.useWatch("deptId", form);
|
const watchedDeptId = Form.useWatch("deptId", form);
|
||||||
const wasOpenRef = useRef(false);
|
const wasOpenRef = useRef(false);
|
||||||
const inflightDeptRef = useRef(null);
|
const inflightDeptRef = useRef(null);
|
||||||
const requestDetailsRef = useRef(requestDetails);
|
|
||||||
requestDetailsRef.current = requestDetails;
|
|
||||||
|
|
||||||
const loadPositionsByDept = async (deptId, currentStaff = null) => {
|
const loadPositionsByDept = async (deptId) => {
|
||||||
const id = asId(deptId);
|
const id = deptId;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
setDeptPositionOptions([]);
|
setDeptPositionOptions([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const key = id;
|
if (inflightDeptRef.current === id) return deptPositionOptions;
|
||||||
if (inflightDeptRef.current === key) {
|
inflightDeptRef.current = id;
|
||||||
return deptPositionOptions;
|
|
||||||
}
|
|
||||||
inflightDeptRef.current = key;
|
|
||||||
setPositionLoading(true);
|
setPositionLoading(true);
|
||||||
try {
|
try {
|
||||||
let res = await fetchOrgPositionPage({
|
const res = await Get("/safetyEval/org-position/page", { current: 1, size: 200, deptId: id });
|
||||||
pageIndex: 1,
|
const options = (res?.data || []).map((p) => ({
|
||||||
pageSize: 200,
|
label: p.positionName,
|
||||||
eqDeptId: id,
|
value: p.id,
|
||||||
});
|
}));
|
||||||
let options = mapPositionOptions(res?.data);
|
|
||||||
// 兼容历史岗位 dept_id 因 Number() 精度丢失与部门 id 不完全一致
|
|
||||||
if (!options.length) {
|
|
||||||
const allRes = await fetchOrgPositionPage({ pageIndex: 1, pageSize: 500 });
|
|
||||||
options = mapPositionOptions(allRes?.data).filter((p) => sameId(p.deptId, id));
|
|
||||||
}
|
|
||||||
const positionId = asId(currentStaff?.positionId);
|
|
||||||
const positionName = currentStaff?.positionName;
|
|
||||||
if (positionId && positionName && !options.some((item) => sameId(item.value, positionId))) {
|
|
||||||
options = [{ label: positionName, value: positionId, deptId: id }, ...options];
|
|
||||||
}
|
|
||||||
setDeptPositionOptions(options);
|
setDeptPositionOptions(options);
|
||||||
return options;
|
return options;
|
||||||
}
|
} catch (err) {
|
||||||
catch (err) {
|
|
||||||
console.warn("[PersonnelInfo] load positions by dept failed:", err);
|
console.warn("[PersonnelInfo] load positions by dept failed:", err);
|
||||||
setDeptPositionOptions([]);
|
setDeptPositionOptions([]);
|
||||||
return [];
|
return [];
|
||||||
}
|
} finally {
|
||||||
finally {
|
if (inflightDeptRef.current === id) inflightDeptRef.current = null;
|
||||||
if (inflightDeptRef.current === key) {
|
|
||||||
inflightDeptRef.current = null;
|
|
||||||
}
|
|
||||||
setPositionLoading(false);
|
setPositionLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -270,51 +275,53 @@ function StaffFormModal({
|
||||||
if (!currentId) {
|
if (!currentId) {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
setDeptPositionOptions([]);
|
setDeptPositionOptions([]);
|
||||||
|
setUploadFileList([]);
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
personType: "基础人员",
|
|
||||||
registerEngineerFlag: 2,
|
registerEngineerFlag: 2,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!open) {
|
if (!open) {
|
||||||
setDeptPositionOptions([]);
|
setDeptPositionOptions([]);
|
||||||
|
setUploadFileList([]);
|
||||||
}
|
}
|
||||||
wasOpenRef.current = open;
|
wasOpenRef.current = open;
|
||||||
}, [open, currentId, form]);
|
}, [open, currentId, form]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open || !currentId) {
|
if (!open || !currentId) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
setDetailLoading(true);
|
setDetailLoading(true);
|
||||||
safeRequest(requestDetailsRef.current, { id: currentId }).then(async (res) => {
|
staffInfoGet({ id: currentId }).then((res) => {
|
||||||
if (!cancelled && res?.data) {
|
if (cancelled || !res?.data) return;
|
||||||
const data = res.data;
|
const data = { ...res.data };
|
||||||
await loadPositionsByDept(data.deptId, data);
|
loadPositionsByDept(data.deptId);
|
||||||
if (!cancelled) {
|
if (cancelled) return;
|
||||||
form.setFieldsValue({
|
const setFields = {
|
||||||
...data,
|
...data,
|
||||||
deptId: asId(data.deptId),
|
};
|
||||||
positionId: asId(data.positionId),
|
if (data.birthDate) setFields.birthDate = dayjs(data.birthDate);
|
||||||
birthDate: toDayjs(data.birthDate),
|
const fileList = data.proofMaterialUrl
|
||||||
proofMaterials: data.proofMaterials?.length ? data.proofMaterials : [],
|
? data.proofMaterialUrl.split(",").filter(Boolean).map((url, i) => ({
|
||||||
});
|
uid: `-${i}`,
|
||||||
}
|
name: url.split("/").pop() || `附件${i + 1}`,
|
||||||
}
|
status: "done",
|
||||||
|
url,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
setFields.proofMaterialUrl = fileList;
|
||||||
|
setUploadFileList(fileList);
|
||||||
|
form.setFieldsValue(setFields);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
if (!cancelled) {
|
if (!cancelled) setDetailLoading(false);
|
||||||
setDetailLoading(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return () => {
|
return () => { cancelled = true; };
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
}, [open, currentId]);
|
}, [open, currentId]);
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
setDeptPositionOptions([]);
|
setDeptPositionOptions([]);
|
||||||
|
setUploadFileList([]);
|
||||||
onCancel();
|
onCancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -322,11 +329,11 @@ function StaffFormModal({
|
||||||
if ("idCardNo" in changed && changed.idCardNo) {
|
if ("idCardNo" in changed && changed.idCardNo) {
|
||||||
const birth = getBirthDateFromIdCard(changed.idCardNo);
|
const birth = getBirthDateFromIdCard(changed.idCardNo);
|
||||||
if (birth) {
|
if (birth) {
|
||||||
form.setFieldValue("birthDate", toDayjs(birth));
|
form.setFieldValue("birthDate", dayjs(birth));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ("deptId" in changed) {
|
if ("deptId" in changed) {
|
||||||
form.setFieldValue("positionId", undefined);
|
form.setFieldValue("postId", undefined);
|
||||||
loadPositionsByDept(changed.deptId);
|
loadPositionsByDept(changed.deptId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -334,21 +341,37 @@ function StaffFormModal({
|
||||||
const handleSubmit = async (values) => {
|
const handleSubmit = async (values) => {
|
||||||
try {
|
try {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
const request = currentId ? requestEdit : requestAdd;
|
const payload = {
|
||||||
if (currentId) {
|
...values,
|
||||||
values.id = currentId;
|
genderName: values.genderCode === 1 ? "男" : values.genderCode === 2 ? "女" : undefined,
|
||||||
|
personTypeCode: values.personTypeName,
|
||||||
|
personTypeName: values.personTypeName,
|
||||||
|
professionalLevelCode: values.professionalLevelName,
|
||||||
|
professionalLevelName: values.professionalLevelName,
|
||||||
|
educationTypeCode: values.educationTypeName,
|
||||||
|
educationTypeName: values.educationTypeName,
|
||||||
|
educationLevelCode: values.educationLevelName,
|
||||||
|
educationLevelName: values.educationLevelName,
|
||||||
|
proofMaterialUrl: Array.isArray(values.proofMaterialUrl)
|
||||||
|
? values.proofMaterialUrl.map((f) => f.url || f.response?.url).filter(Boolean).join(",")
|
||||||
|
: "",
|
||||||
|
};
|
||||||
|
if (payload.birthDate && typeof payload.birthDate !== "string") {
|
||||||
|
payload.birthDate = payload.birthDate.format("YYYY-MM-DD");
|
||||||
}
|
}
|
||||||
const res = await request(values);
|
if (currentId) payload.id = currentId;
|
||||||
|
delete payload.proofMaterials;
|
||||||
|
|
||||||
|
const request = currentId ? staffInfoEdit : staffInfoAdd;
|
||||||
|
const res = await request(payload);
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
message.success(currentId ? "编辑成功" : "添加成功");
|
message.success(currentId ? "编辑成功" : "添加成功");
|
||||||
handleCancel();
|
handleCancel();
|
||||||
onSuccess();
|
onSuccess();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
message.error(res?.message || "操作失败");
|
message.error(res?.message || "操作失败");
|
||||||
}
|
}
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -372,12 +395,12 @@ function StaffFormModal({
|
||||||
>
|
>
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="staffName" label="姓名" rules={[{ required: true, message: "请输入姓名" }]}>
|
<Form.Item name="userName" label="姓名" rules={[{ required: true, message: "请输入姓名" }]}>
|
||||||
<Input placeholder="请输入姓名" />
|
<Input placeholder="请输入姓名" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="gender" label="性别" rules={[{ required: true, message: "请选择性别" }]}>
|
<Form.Item name="genderCode" label="性别" rules={[{ required: true, message: "请选择性别" }]}>
|
||||||
<Select
|
<Select
|
||||||
placeholder="请选择性别"
|
placeholder="请选择性别"
|
||||||
options={[{ label: "男", value: 1 }, { label: "女", value: 2 }]}
|
options={[{ label: "男", value: 1 }, { label: "女", value: 2 }]}
|
||||||
|
|
@ -400,7 +423,7 @@ function StaffFormModal({
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="positionId" label="岗位" rules={[{ required: true, message: "请选择岗位" }]}>
|
<Form.Item name="postId" label="岗位" rules={[{ required: true, message: "请选择岗位" }]}>
|
||||||
<Select
|
<Select
|
||||||
placeholder="请选择岗位"
|
placeholder="请选择岗位"
|
||||||
options={deptPositionOptions}
|
options={deptPositionOptions}
|
||||||
|
|
@ -418,7 +441,7 @@ function StaffFormModal({
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="personType" label="人员类型" initialValue="基础人员">
|
<Form.Item name="personTypeName" label="人员类型">
|
||||||
<Select placeholder="请选择人员类型" options={PERSON_TYPE_OPTIONS} />
|
<Select placeholder="请选择人员类型" options={PERSON_TYPE_OPTIONS} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
@ -428,7 +451,7 @@ function StaffFormModal({
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="professionalLevel" label="职业等级">
|
<Form.Item name="professionalLevelName" label="职业等级">
|
||||||
<Select placeholder="请选择职业等级" allowClear options={PROFESSIONAL_LEVEL_OPTIONS} />
|
<Select placeholder="请选择职业等级" allowClear options={PROFESSIONAL_LEVEL_OPTIONS} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
@ -438,12 +461,12 @@ function StaffFormModal({
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="educationType" label="学历类型">
|
<Form.Item name="educationTypeName" label="学历类型">
|
||||||
<Select placeholder="请选择学历类型" allowClear options={EDUCATION_TYPE_OPTIONS} />
|
<Select placeholder="请选择学历类型" allowClear options={EDUCATION_TYPE_OPTIONS} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="educationLevel" label="学历层次">
|
<Form.Item name="educationLevelName" label="学历层次">
|
||||||
<Select placeholder="请选择学历层次" allowClear options={EDUCATION_LEVEL_OPTIONS} />
|
<Select placeholder="请选择学历层次" allowClear options={EDUCATION_LEVEL_OPTIONS} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
@ -473,12 +496,24 @@ function StaffFormModal({
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Form.Item name="proofMaterials" label="申报专业能力证明材料">
|
<Form.Item name="proofMaterialUrl" label="申报专业能力证明材料" valuePropName="fileList" getValueFromEvent={(e) => {
|
||||||
<Upload maxCount={5} />
|
if (Array.isArray(e)) return e;
|
||||||
|
return e?.fileList;
|
||||||
|
}}>
|
||||||
|
<Upload
|
||||||
|
action="/api/upload"
|
||||||
|
maxCount={5}
|
||||||
|
fileList={uploadFileList}
|
||||||
|
onChange={(info) => {
|
||||||
|
setUploadFileList(info.fileList);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button icon={<UploadOutlined />}>上传文件</Button>
|
||||||
|
</Upload>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="homeAddress" label="现住地址">
|
<Form.Item name="currentAddress" label="现住地址">
|
||||||
<Input placeholder="请输入现住地址" />
|
<Input placeholder="请输入现住地址" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
@ -497,15 +532,10 @@ function StaffFormModal({
|
||||||
<Input placeholder="请输入专业" />
|
<Input placeholder="请输入专业" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
|
||||||
<Form.Item name="joinWorkDate" label="参加工作日期">
|
|
||||||
<DatePicker placeholder="请选择参加工作日期" />
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
</Row>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Connect([NS_STAFF_INFO, NS_ORG_DEPARTMENT, NS_ORG_POSITION], true)(PersonnelInfoPage);
|
export default Connect([NS_STAFF_INFO], true)(AntdTableFuncControl(PersonnelInfoPage));
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import { Button, Descriptions, Modal, Space, Table } from "antd";
|
import { Button, Descriptions, Modal, Table } from "antd";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import FilePreviewModal, { FilePreviewContent } from "~/components/FilePreviewModal";
|
import FilePreviewModal, { FilePreviewContent } from "~/components/FilePreviewModal";
|
||||||
import { resolvePreviewSrc } from "~/components/CertPreviewImg";
|
import { resolvePreviewSrc } from "~/components/CertPreviewImg";
|
||||||
import { fetchStaffCertDetail, fetchStaffCertListByPersonnelId } from "~/api/staffCertificate";
|
import { fetchStaffCertDetail, fetchStaffCertListByPersonnelId } from "~/api/staffCertificate";
|
||||||
import { fetchOrgPersonnelDetail } from "~/api/qualFiling/personnelHelper";
|
import { fetchOrgPersonnelDetail } from "~/api/qualFiling/personnelHelper";
|
||||||
import { safeRequest } from "~/utils";
|
|
||||||
|
|
||||||
const GENDER_MAP = { 1: "男", 2: "女" };
|
const GENDER_MAP = { 1: "男", 2: "女" };
|
||||||
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
|
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
|
||||||
|
|
@ -39,9 +38,13 @@ export default function StaffViewModal({
|
||||||
}
|
}
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
setDetailLoading(true);
|
setDetailLoading(true);
|
||||||
|
|
||||||
const loadCertList = requestCertListRef.current;
|
const loadCertList = requestCertListRef.current;
|
||||||
Promise.all([
|
Promise.all([
|
||||||
safeRequest(requestDetailsRef.current, { id: currentId }),
|
Promise.resolve(requestDetailsRef.current({ id: currentId })).catch((err) => {
|
||||||
|
console.warn("[StaffViewModal] load detail failed:", err);
|
||||||
|
return { data: {} };
|
||||||
|
}),
|
||||||
typeof loadCertList === "function"
|
typeof loadCertList === "function"
|
||||||
? Promise.resolve(loadCertList(currentId)).catch((err) => {
|
? Promise.resolve(loadCertList(currentId)).catch((err) => {
|
||||||
console.warn("[StaffViewModal] load certificates failed:", err);
|
console.warn("[StaffViewModal] load certificates failed:", err);
|
||||||
|
|
@ -72,10 +75,12 @@ export default function StaffViewModal({
|
||||||
}
|
}
|
||||||
setCertPreviewLoading(true);
|
setCertPreviewLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await safeRequest(requestCertDetailsRef.current, { id: record.id });
|
const res = await requestCertDetailsRef.current({ id: record.id });
|
||||||
setCertPreviewInfo(res?.data || record);
|
setCertPreviewInfo(res?.data || record);
|
||||||
}
|
} catch (err) {
|
||||||
finally {
|
console.warn("[StaffViewModal] load cert detail failed:", err);
|
||||||
|
setCertPreviewInfo(record);
|
||||||
|
} finally {
|
||||||
setCertPreviewLoading(false);
|
setCertPreviewLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -133,11 +138,11 @@ export default function StaffViewModal({
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="申报专业能力证明材料" span={2}>
|
<Descriptions.Item label="申报专业能力证明材料" span={2}>
|
||||||
{proofFiles.length ? (
|
{proofFiles.length ? (
|
||||||
<Space direction="vertical" size={4}>
|
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
|
||||||
{proofFiles.map((file, index) => {
|
{proofFiles.map((file, index) => {
|
||||||
const fileName = file.name || file.fileName || `专业能力证明${index + 1}.pdf`;
|
const fileName = file.name || file.fileName || `专业能力证明${index + 1}.pdf`;
|
||||||
return (
|
return (
|
||||||
<Space key={`${fileName}-${index}`}>
|
<div key={`${fileName}-${index}`} style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
size="small"
|
||||||
|
|
@ -147,10 +152,10 @@ export default function StaffViewModal({
|
||||||
查看附件
|
查看附件
|
||||||
</Button>
|
</Button>
|
||||||
<span>{fileName}</span>
|
<span>{fileName}</span>
|
||||||
</Space>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Space>
|
</div>
|
||||||
) : "-"}
|
) : "-"}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
|
@ -226,4 +231,4 @@ export default function StaffViewModal({
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,20 +1,35 @@
|
||||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import { Button, DatePicker, Descriptions, Form, Input, message, Modal, Select } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
DatePicker,
|
||||||
|
Descriptions,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
message,
|
||||||
|
Modal,
|
||||||
|
Select,
|
||||||
|
Table,
|
||||||
|
Tag,
|
||||||
|
Upload,
|
||||||
|
} from "antd";
|
||||||
|
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||||
|
import { UploadOutlined } from "@ant-design/icons";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import AddIcon from "zy-react-library/components/Icon/AddIcon";
|
|
||||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||||
import Search from "zy-react-library/components/Search";
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
||||||
import Table from "zy-react-library/components/Table";
|
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
|
||||||
import Upload from "zy-react-library/components/Upload";
|
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
||||||
import useTable from "zy-react-library/hooks/useTable";
|
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||||
import { asId } from "~/api/enterpriseInfo/idUtil";
|
import {
|
||||||
import { NS_STAFF_INFO, NS_STAFF_RESIGNATION_APPLY } from "~/enumerate/namespace";
|
NS_STAFF_INFO,
|
||||||
import { safeListRequest, safeRequest } from "~/utils";
|
NS_STAFF_RESIGNATION_APPLY,
|
||||||
import { formSelectField, getResignAuditStatusLabel } from "~/utils/enterpriseForm";
|
} from "~/enumerate/namespace";
|
||||||
import { DEFAULT_UPLOAD_FILE_URL, mockUploadFileList, resolveUploadFileId } from "~/utils/mockUpload";
|
import {
|
||||||
|
RESIGN_AUDIT_STATUS_LABEL,
|
||||||
|
AUDIT_STATUS_COLOR,
|
||||||
|
} from "~/utils/enterpriseForm";
|
||||||
|
|
||||||
|
const { router } = tools;
|
||||||
const RESIGN_AUDIT_STATUS = { 0: "未审核", 1: "已审核", 2: "已退回" };
|
|
||||||
|
|
||||||
function ResignationApplyPage(props) {
|
function ResignationApplyPage(props) {
|
||||||
const [addModalOpen, setAddModalOpen] = useState(false);
|
const [addModalOpen, setAddModalOpen] = useState(false);
|
||||||
|
|
@ -22,27 +37,39 @@ function ResignationApplyPage(props) {
|
||||||
const [currentId, setCurrentId] = useState("");
|
const [currentId, setCurrentId] = useState("");
|
||||||
const [staffOptions, setStaffOptions] = useState([]);
|
const [staffOptions, setStaffOptions] = useState([]);
|
||||||
const [searchForm] = Form.useForm();
|
const [searchForm] = Form.useForm();
|
||||||
|
const { staffResignationApply } = props;
|
||||||
|
const {
|
||||||
|
staffResignationApplyList: dataSource,
|
||||||
|
staffResignationApplyTotal: total,
|
||||||
|
staffResignationApplyLoading: loading,
|
||||||
|
} = staffResignationApply || {};
|
||||||
|
|
||||||
const { tableProps, getData } = useTable(safeListRequest(props.staffResignationApplyList), { form: searchForm });
|
const handleSearch = () => {
|
||||||
|
props.staffResignationApplyList({ ...router.query });
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getData();
|
searchForm.setFieldsValue(router.query);
|
||||||
|
handleSearch();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
props.staffInfoList?.({ pageIndex: 1, pageSize: 500 }).then((res) => {
|
props
|
||||||
if (!cancelled) {
|
.staffInfoList?.({ current: 1, size: 500 })
|
||||||
setStaffOptions((res?.data || []).map((s) => ({
|
.then((res) => {
|
||||||
label: `${s.staffName}(${s.account})`,
|
if (!cancelled) {
|
||||||
value: asId(s.id),
|
setStaffOptions(
|
||||||
staffName: s.staffName,
|
(res?.data || []).map((s) => ({
|
||||||
account: s.account,
|
label: `${s.userName ?? s.staffName}(${s.account})`,
|
||||||
})));
|
value: s.id,
|
||||||
}
|
staffName: s.userName ?? s.staffName,
|
||||||
}).catch((err) => {
|
account: s.account,
|
||||||
console.warn("[ResignationApply] load staff options failed:", err);
|
})),
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
|
|
@ -58,82 +85,126 @@ function ResignationApplyPage(props) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
extra={
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
style={{ marginTop: 16 }}
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentId("");
|
||||||
|
setAddModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
新增离职申请
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Search
|
<SearchForm
|
||||||
|
style={{ marginBottom: 24 }}
|
||||||
form={searchForm}
|
form={searchForm}
|
||||||
options={[
|
loading={loading}
|
||||||
{ name: "likeStaffName", label: "姓名", placeholder: "请输入姓名", colProps: { xs: 24, sm: 12, md: 8, lg: 6 } },
|
formLine={[
|
||||||
formSelectField(
|
<Form.Item key="applicantName" name="applicantName">
|
||||||
"auditStatus",
|
<ControlWrapper.Input
|
||||||
"离职申请审核状态",
|
label="姓名"
|
||||||
Object.entries(RESIGN_AUDIT_STATUS).map(([value, label]) => ({ label, value: Number(value) })),
|
placeholder="请输入"
|
||||||
{ colProps: { xs: 24, sm: 12, md: 8, lg: 8 }, placeholder: "请选择审核状态" },
|
allowClear
|
||||||
),
|
/>
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item key="auditStatusCode" name="auditStatusCode">
|
||||||
|
<ControlWrapper.Select
|
||||||
|
label="离职申请审核状态"
|
||||||
|
placeholder="请选择"
|
||||||
|
allowClear
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{Object.entries(RESIGN_AUDIT_STATUS_LABEL).map(
|
||||||
|
([value, label]) => (
|
||||||
|
<Select.Option key={value} value={Number(value)}>
|
||||||
|
{label}
|
||||||
|
</Select.Option>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</ControlWrapper.Select>
|
||||||
|
</Form.Item>,
|
||||||
]}
|
]}
|
||||||
onFinish={getData}
|
onReset={() => {
|
||||||
|
router.query = { ...router.query, current: 1, size: 10 };
|
||||||
|
handleSearch();
|
||||||
|
}}
|
||||||
|
onFinish={() => {
|
||||||
|
router.query = { ...router.query, current: 1, size: 10 };
|
||||||
|
handleSearch();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Table
|
<Table
|
||||||
toolBarRender={() => (
|
rowKey="id"
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
icon={<AddIcon />}
|
|
||||||
onClick={() => {
|
|
||||||
setCurrentId("");
|
|
||||||
setAddModalOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
新增离职申请
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
columns={[
|
columns={[
|
||||||
{ title: "账户", dataIndex: "account" },
|
{ title: "账户", dataIndex: "account", width: 140 },
|
||||||
{ title: "姓名", dataIndex: "applicantName" },
|
{ title: "姓名", dataIndex: "applicantName", width: 100 },
|
||||||
{ title: "部门", dataIndex: "deptName" },
|
{ title: "部门", dataIndex: "deptName", width: 160 },
|
||||||
{ title: "申请时间", dataIndex: "applyTime" },
|
{ title: "申请时间", dataIndex: "applyTime", width: 160 },
|
||||||
{
|
{
|
||||||
title: "离职申请审核状态",
|
title: "离职申请审核状态",
|
||||||
dataIndex: "auditStatus",
|
dataIndex: "auditStatusCode",
|
||||||
render: (_, record) => getResignAuditStatusLabel(record),
|
width: 140,
|
||||||
|
render: (_, record) => {
|
||||||
|
const code = record.auditStatusCode ?? record.auditStatus;
|
||||||
|
const label = RESIGN_AUDIT_STATUS_LABEL[code] ?? "-";
|
||||||
|
return <Tag color={AUDIT_STATUS_COLOR[code]}>{label}</Tag>;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "操作",
|
title: "操作",
|
||||||
width: 100,
|
width: 80,
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Button
|
<TableAction>
|
||||||
type="link"
|
<Button
|
||||||
onClick={() => {
|
type="link"
|
||||||
setCurrentId(asId(record.id));
|
size="small"
|
||||||
setViewModalOpen(true);
|
onClick={() => {
|
||||||
}}
|
setCurrentId(record.id);
|
||||||
>
|
setViewModalOpen(true);
|
||||||
查看
|
}}
|
||||||
</Button>
|
>
|
||||||
|
查看
|
||||||
|
</Button>
|
||||||
|
</TableAction>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
{...tableProps}
|
dataSource={dataSource}
|
||||||
|
scroll={{ y: props.scrollY }}
|
||||||
|
loading={loading}
|
||||||
|
pagination={{
|
||||||
|
total,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showTotal: (t) => `共 ${t} 条`,
|
||||||
|
current: router.query.current || 1,
|
||||||
|
pageSize: router.query.size || 10,
|
||||||
|
onChange: (page, pageSize) => {
|
||||||
|
router.query = { ...router.query, current: page, size: pageSize };
|
||||||
|
handleSearch();
|
||||||
|
},
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{addModalOpen && (
|
<AddModal
|
||||||
<AddModal
|
open={addModalOpen}
|
||||||
open={addModalOpen}
|
staffOptions={staffOptions}
|
||||||
staffOptions={staffOptions}
|
requestAdd={props.staffResignationApplyAdd}
|
||||||
requestAdd={props.staffResignationApplyAdd}
|
onCancel={() => setAddModalOpen(false)}
|
||||||
onCancel={() => setAddModalOpen(false)}
|
onSuccess={handleSearch}
|
||||||
onSuccess={getData}
|
/>
|
||||||
/>
|
<ViewModal
|
||||||
)}
|
open={viewModalOpen}
|
||||||
{viewModalOpen && (
|
currentId={currentId}
|
||||||
<ViewModal
|
requestDetail={props.staffResignationApplyInfo}
|
||||||
open={viewModalOpen}
|
onCancel={() => {
|
||||||
currentId={currentId}
|
setViewModalOpen(false);
|
||||||
requestDetails={props.staffResignationApplyInfo}
|
setCurrentId("");
|
||||||
onCancel={() => {
|
}}
|
||||||
setViewModalOpen(false);
|
/>
|
||||||
setCurrentId("");
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -141,36 +212,33 @@ function ResignationApplyPage(props) {
|
||||||
function AddModal({ open, staffOptions, requestAdd, onCancel, onSuccess }) {
|
function AddModal({ open, staffOptions, requestAdd, onCancel, onSuccess }) {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
const [uploading, setUploading] = useState(false);
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
onCancel();
|
onCancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onApplicantChange = (personnelId) => {
|
const handleSubmit = async () => {
|
||||||
const staff = staffOptions.find((s) => String(s.value) === String(personnelId));
|
|
||||||
form.setFieldsValue({
|
|
||||||
applicantName: staff?.staffName || "",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (values) => {
|
|
||||||
try {
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
const staff = staffOptions.find((s) => String(s.value) === String(values.personnelId));
|
values.applyTime =
|
||||||
values.applicantName = staff?.staffName || values.applicantName;
|
values.applyTime?.format?.("YYYY-MM-DD HH:mm:ss") || values.applyTime;
|
||||||
values.attachmentId = resolveUploadFileId(values.resignReport);
|
values.expectedResignDate =
|
||||||
|
values.expectedResignDate?.format?.("YYYY-MM-DD") ||
|
||||||
|
values.expectedResignDate;
|
||||||
const res = await requestAdd(values);
|
const res = await requestAdd(values);
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
message.success("提交成功");
|
message.success("提交成功");
|
||||||
handleCancel();
|
handleCancel();
|
||||||
onSuccess();
|
onSuccess();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
message.error(res?.message || "提交失败");
|
message.error(res?.message || "提交失败");
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
finally {
|
// validation error
|
||||||
|
} finally {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -183,9 +251,9 @@ function AddModal({ open, staffOptions, requestAdd, onCancel, onSuccess }) {
|
||||||
width={720}
|
width={720}
|
||||||
confirmLoading={submitting}
|
confirmLoading={submitting}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
onOk={form.submit}
|
onOk={handleSubmit}
|
||||||
>
|
>
|
||||||
<Form form={form} layout="vertical" onFinish={handleSubmit}>
|
<Form form={form} layout="vertical">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="personnelId"
|
name="personnelId"
|
||||||
label="申请人"
|
label="申请人"
|
||||||
|
|
@ -196,12 +264,8 @@ function AddModal({ open, staffOptions, requestAdd, onCancel, onSuccess }) {
|
||||||
placeholder="请选择申请人"
|
placeholder="请选择申请人"
|
||||||
options={staffOptions}
|
options={staffOptions}
|
||||||
optionFilterProp="label"
|
optionFilterProp="label"
|
||||||
onChange={onApplicantChange}
|
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="applicantName" hidden>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="applyTime"
|
name="applyTime"
|
||||||
label="申请时间"
|
label="申请时间"
|
||||||
|
|
@ -210,14 +274,14 @@ function AddModal({ open, staffOptions, requestAdd, onCancel, onSuccess }) {
|
||||||
<DatePicker showTime style={{ width: "100%" }} />
|
<DatePicker showTime style={{ width: "100%" }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="expectedLeaveDate"
|
name="expectedResignDate"
|
||||||
label="预计离职日期"
|
label="预计离职日期"
|
||||||
rules={[{ required: true, message: "请选择预计离职日期" }]}
|
rules={[{ required: true, message: "请选择预计离职日期" }]}
|
||||||
>
|
>
|
||||||
<DatePicker style={{ width: "100%" }} />
|
<DatePicker style={{ width: "100%" }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="leaveReason"
|
name="resignReason"
|
||||||
label="离职原因"
|
label="离职原因"
|
||||||
rules={[{ required: true, message: "请输入离职原因" }]}
|
rules={[{ required: true, message: "请输入离职原因" }]}
|
||||||
>
|
>
|
||||||
|
|
@ -227,25 +291,40 @@ function AddModal({ open, staffOptions, requestAdd, onCancel, onSuccess }) {
|
||||||
<Input.TextArea rows={2} placeholder="请输入备注" />
|
<Input.TextArea rows={2} placeholder="请输入备注" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="resignReport"
|
name="reportFileUrl"
|
||||||
label="离职通知报告"
|
label="离职通知报告"
|
||||||
rules={[{ required: true, message: "请上传离职通知报告" }]}
|
rules={[{ required: true, message: "请上传离职通知报告" }]}
|
||||||
valuePropName="fileList"
|
|
||||||
getValueFromEvent={(e) => e?.fileList}
|
|
||||||
initialValue={mockUploadFileList("离职通知报告.pdf")}
|
|
||||||
>
|
>
|
||||||
<Upload
|
<Upload
|
||||||
accept=".pdf"
|
action={`${window.process.env.app.API_HOST}/safetyEval/file/upload`}
|
||||||
|
showUploadList={false}
|
||||||
maxCount={1}
|
maxCount={1}
|
||||||
tip="联调阶段默认通过,使用固定文件地址"
|
accept=".pdf"
|
||||||
/>
|
onChange={(info) => {
|
||||||
|
if (info.file.status === "uploading") {
|
||||||
|
setUploading(true);
|
||||||
|
} else {
|
||||||
|
setUploading(false);
|
||||||
|
if (info.file.status === "done") {
|
||||||
|
const data = info.file.response?.data;
|
||||||
|
if (data) {
|
||||||
|
form.setFieldsValue({ reportFileUrl: data.url || data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button loading={uploading} icon={<UploadOutlined />}>
|
||||||
|
上传文件
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ViewModal({ open, currentId, requestDetails, onCancel }) {
|
function ViewModal({ open, currentId, requestDetail, onCancel }) {
|
||||||
const [info, setInfo] = useState({});
|
const [info, setInfo] = useState({});
|
||||||
const [detailLoading, setDetailLoading] = useState(false);
|
const [detailLoading, setDetailLoading] = useState(false);
|
||||||
|
|
||||||
|
|
@ -256,21 +335,13 @@ function ViewModal({ open, currentId, requestDetails, onCancel }) {
|
||||||
}
|
}
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
setDetailLoading(true);
|
setDetailLoading(true);
|
||||||
safeRequest(requestDetails, { id: currentId }).then((res) => {
|
requestDetail({ id: currentId })
|
||||||
if (!cancelled) {
|
.then((res) => {
|
||||||
const data = res?.data || {};
|
if (!cancelled) setInfo(res?.data || {});
|
||||||
setInfo({
|
})
|
||||||
...data,
|
.finally(() => {
|
||||||
resignReport: data.attachmentId
|
if (!cancelled) setDetailLoading(false);
|
||||||
? [{ name: "离职通知报告.pdf", fileName: "离职通知报告.pdf", url: data.attachmentId || DEFAULT_UPLOAD_FILE_URL }]
|
});
|
||||||
: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).finally(() => {
|
|
||||||
if (!cancelled) {
|
|
||||||
setDetailLoading(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
|
|
@ -288,17 +359,26 @@ function ViewModal({ open, currentId, requestDetails, onCancel }) {
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
>
|
>
|
||||||
<Descriptions bordered column={1} labelStyle={{ width: 120 }}>
|
<Descriptions bordered column={1} labelStyle={{ width: 120 }}>
|
||||||
<Descriptions.Item label="申请人">{info.applicantName}</Descriptions.Item>
|
<Descriptions.Item label="申请人">
|
||||||
|
{info.applicantName}
|
||||||
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="申请时间">{info.applyTime}</Descriptions.Item>
|
<Descriptions.Item label="申请时间">{info.applyTime}</Descriptions.Item>
|
||||||
<Descriptions.Item label="预计离职日期">{info.expectedLeaveDate}</Descriptions.Item>
|
<Descriptions.Item label="预计离职日期">
|
||||||
<Descriptions.Item label="离职原因">{info.leaveReason}</Descriptions.Item>
|
{info.expectedResignDate}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label="离职原因">
|
||||||
|
{info.resignReason}
|
||||||
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="备注">{info.remark}</Descriptions.Item>
|
<Descriptions.Item label="备注">{info.remark}</Descriptions.Item>
|
||||||
<Descriptions.Item label="离职通知报告">
|
<Descriptions.Item label="离职通知报告">
|
||||||
{info.resignReport?.[0]?.fileName || info.resignReport?.[0]?.name || "-"}
|
{info.reportFileUrl || "-"}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Connect([NS_STAFF_RESIGNATION_APPLY, NS_STAFF_INFO], true)(ResignationApplyPage);
|
export default Connect(
|
||||||
|
[NS_STAFF_RESIGNATION_APPLY, NS_STAFF_INFO],
|
||||||
|
true,
|
||||||
|
)(AntdTableFuncControl(ResignationApplyPage));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue