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