代码提交
parent
c31b85c4c1
commit
de5a1a4dc6
|
|
@ -10,10 +10,10 @@ module.exports = {
|
||||||
javaGitBranch: "dev",
|
javaGitBranch: "dev",
|
||||||
// 本地联调 safetyEval-service(context-path: /safetyEval,默认端口 8095)
|
// 本地联调 safetyEval-service(context-path: /safetyEval,默认端口 8095)
|
||||||
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
|
// 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095
|
||||||
// API_HOST: "https://gbs-gateway.qhdsafety.com",
|
API_HOST: "http://localhost:80",
|
||||||
|
|
||||||
// API_HOST: "http://192.168.0.134",
|
// API_HOST: "http://192.168.0.134",
|
||||||
API_HOST: "http://192.168.0.150", //太浅
|
// API_HOST: "http://192.168.0.150", //太浅
|
||||||
// API_HOST: "http://192.168.0.152",
|
// API_HOST: "http://192.168.0.152",
|
||||||
//API_HOST: "http://192.168.0.103", //huwei
|
//API_HOST: "http://192.168.0.103", //huwei
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,12 @@ export const staffInfoList = declareRequest(
|
||||||
"staffInfoList: [] | res.data || [] & staffInfoTotal: 0 | res.total || 0",
|
"staffInfoList: [] | res.data || [] & staffInfoTotal: 0 | res.total || 0",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const staffInfoDeptPersonCount = declareRequest(
|
||||||
|
"staffInfoLoading",
|
||||||
|
"Get > /safetyEval/org-personnel/deptPersonCount",
|
||||||
|
"staffInfoDeptPersonCount: [] | res.data || []",
|
||||||
|
);
|
||||||
|
|
||||||
export const staffInfoGet = declareRequest(
|
export const staffInfoGet = declareRequest(
|
||||||
"staffInfoLoading",
|
"staffInfoLoading",
|
||||||
"Get > /safetyEval/org-personnel/get",
|
"Get > /safetyEval/org-personnel/get",
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,14 @@ import {
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Table,
|
Table,
|
||||||
|
Tree,
|
||||||
TreeSelect,
|
TreeSelect,
|
||||||
Upload,
|
Upload,
|
||||||
Tag,
|
Tag,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import { InboxOutlined } from "@ant-design/icons";
|
import { InboxOutlined } from "@ant-design/icons";
|
||||||
import { Get } from "@cqsjjb/jjb-common-lib/http";
|
import { Get } from "@cqsjjb/jjb-common-lib/http";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||||
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
||||||
|
|
@ -49,11 +50,13 @@ import { idCardRule, mobileRule } from "~/utils/validators";
|
||||||
import AttachmentUpload from "~/components/AttachmentUpload";
|
import AttachmentUpload from "~/components/AttachmentUpload";
|
||||||
import StaffViewModal from "~/components/StaffViewModal";
|
import StaffViewModal from "~/components/StaffViewModal";
|
||||||
import StaffResumeModal from "~/components/StaffResumeModal";
|
import StaffResumeModal from "~/components/StaffResumeModal";
|
||||||
|
import { asId } from "~/utils/enterpriseInfo/idUtil";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
const { router } = tools;
|
const { router } = tools;
|
||||||
const API_HOST = window.process?.env?.app?.API_HOST || "";
|
const API_HOST = window.process?.env?.app?.API_HOST || "";
|
||||||
const evalCertLevelMap = CERT_LEVEL_LABEL_BY_TYPE.evaluator;
|
const evalCertLevelMap = CERT_LEVEL_LABEL_BY_TYPE.evaluator;
|
||||||
|
const ALL_DEPT_KEY = "ALL";
|
||||||
|
|
||||||
function PersonnelInfoPage(props) {
|
function PersonnelInfoPage(props) {
|
||||||
const [formModalOpen, setFormModalOpen] = useState(false);
|
const [formModalOpen, setFormModalOpen] = useState(false);
|
||||||
|
|
@ -65,6 +68,11 @@ 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 [treeData, setTreeData] = useState([]);
|
||||||
|
const [deptCountMap, setDeptCountMap] = useState({});
|
||||||
|
const [selectedDeptId, setSelectedDeptId] = useState(
|
||||||
|
asId(router.query.deptId) || ALL_DEPT_KEY,
|
||||||
|
);
|
||||||
const { staffInfo, orgDepartmentTree, orgDepartment } = props;
|
const { staffInfo, orgDepartmentTree, orgDepartment } = props;
|
||||||
const { orgDepartmentTreeData } = orgDepartment;
|
const { orgDepartmentTreeData } = orgDepartment;
|
||||||
const {
|
const {
|
||||||
|
|
@ -73,8 +81,36 @@ function PersonnelInfoPage(props) {
|
||||||
staffInfoLoading: loading,
|
staffInfoLoading: loading,
|
||||||
} = staffInfo || {};
|
} = staffInfo || {};
|
||||||
|
|
||||||
|
const loadDeptPersonCount = async () => {
|
||||||
|
try {
|
||||||
|
const res = await props.staffInfoDeptPersonCount?.();
|
||||||
|
const map = {};
|
||||||
|
(res?.data || []).forEach((item) => {
|
||||||
|
const id = asId(item.deptId);
|
||||||
|
if (id) {
|
||||||
|
map[id] = Number(item.personCount) || 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setDeptCountMap(map);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("[PersonnelInfo] load deptPersonCount failed:", err);
|
||||||
|
setDeptCountMap({});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadTree = async () => {
|
||||||
|
try {
|
||||||
|
const res = await orgDepartmentTree();
|
||||||
|
setTreeData(res?.data || []);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("[PersonnelInfo] loadTree failed:", err);
|
||||||
|
setTreeData([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
orgDepartmentTree();
|
loadTree();
|
||||||
|
loadDeptPersonCount();
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -100,20 +136,62 @@ function PersonnelInfoPage(props) {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
searchForm.setFieldsValue(router.query);
|
const { deptId: _ignored, ...rest } = router.query || {};
|
||||||
|
searchForm.setFieldsValue(rest);
|
||||||
handleSearch();
|
handleSearch();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
props.staffInfoList({ ...router.query });
|
const deptId =
|
||||||
|
selectedDeptId && selectedDeptId !== ALL_DEPT_KEY
|
||||||
|
? asId(selectedDeptId)
|
||||||
|
: undefined;
|
||||||
|
props.staffInfoList({
|
||||||
|
...router.query,
|
||||||
|
deptId,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const goCertificate = (id, staffName) => {
|
const refreshListAndCount = () => {
|
||||||
props.history.push(
|
handleSearch();
|
||||||
`Certificate?staffId=${id}&staffName=${encodeURIComponent(staffName || "")}`,
|
loadDeptPersonCount();
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const applyDeptFilter = (deptId) => {
|
||||||
|
const nextId = deptId || ALL_DEPT_KEY;
|
||||||
|
setSelectedDeptId(nextId);
|
||||||
|
const queryDeptId = nextId === ALL_DEPT_KEY ? undefined : asId(nextId);
|
||||||
|
router.query = {
|
||||||
|
...router.query,
|
||||||
|
deptId: queryDeptId,
|
||||||
|
current: 1,
|
||||||
|
};
|
||||||
|
props.staffInfoList({
|
||||||
|
...router.query,
|
||||||
|
deptId: queryDeptId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayTree = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
id: ALL_DEPT_KEY,
|
||||||
|
deptName: "全部人员",
|
||||||
|
children: treeData,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[treeData],
|
||||||
|
);
|
||||||
|
|
||||||
|
const totalPersonCount = useMemo(
|
||||||
|
() =>
|
||||||
|
Object.values(deptCountMap).reduce(
|
||||||
|
(sum, n) => sum + (Number(n) || 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
[deptCountMap],
|
||||||
|
);
|
||||||
|
|
||||||
const onDelete = (id) => {
|
const onDelete = (id) => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: "提示",
|
title: "提示",
|
||||||
|
|
@ -124,7 +202,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("删除成功");
|
||||||
handleSearch();
|
refreshListAndCount();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -160,6 +238,31 @@ function PersonnelInfoPage(props) {
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<div className="personnel-info-layout">
|
||||||
|
<div className="personnel-info-tree">
|
||||||
|
<div className="personnel-info-tree-title">组织机构</div>
|
||||||
|
<Tree
|
||||||
|
key={treeData.length ? "dept-tree-loaded" : "dept-tree-empty"}
|
||||||
|
selectedKeys={[asId(selectedDeptId) || ALL_DEPT_KEY]}
|
||||||
|
treeData={displayTree}
|
||||||
|
defaultExpandAll
|
||||||
|
fieldNames={{ title: "deptName", key: "id", children: "children" }}
|
||||||
|
titleRender={(node) => {
|
||||||
|
if (node.id === ALL_DEPT_KEY) {
|
||||||
|
return `全部人员 (${totalPersonCount}人)`;
|
||||||
|
}
|
||||||
|
const count = deptCountMap[asId(node.id)] ?? 0;
|
||||||
|
return `${node.deptName || ""} (${count}人)`;
|
||||||
|
}}
|
||||||
|
onSelect={(keys, { node }) => {
|
||||||
|
if (!keys?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applyDeptFilter(node.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="personnel-info-main">
|
||||||
<SearchForm
|
<SearchForm
|
||||||
style={{ marginBottom: 24 }}
|
style={{ marginBottom: 24 }}
|
||||||
form={searchForm}
|
form={searchForm}
|
||||||
|
|
@ -177,20 +280,6 @@ function PersonnelInfoPage(props) {
|
||||||
maxLength={50}
|
maxLength={50}
|
||||||
/>
|
/>
|
||||||
</Form.Item>,
|
</Form.Item>,
|
||||||
<Form.Item key="deptId" name="deptId">
|
|
||||||
<ControlWrapper.TreeSelect
|
|
||||||
placeholder="请选择部门"
|
|
||||||
treeData={orgDepartmentTreeData}
|
|
||||||
allowClear
|
|
||||||
fieldNames={{
|
|
||||||
label: "deptName",
|
|
||||||
value: "id",
|
|
||||||
key: "id",
|
|
||||||
}}
|
|
||||||
treeDefaultExpandAll
|
|
||||||
showSearch
|
|
||||||
></ControlWrapper.TreeSelect>
|
|
||||||
</Form.Item>,
|
|
||||||
<Form.Item key="postId" name="postId">
|
<Form.Item key="postId" name="postId">
|
||||||
<ControlWrapper.Select
|
<ControlWrapper.Select
|
||||||
label="岗位"
|
label="岗位"
|
||||||
|
|
@ -207,11 +296,19 @@ function PersonnelInfoPage(props) {
|
||||||
</Form.Item>,
|
</Form.Item>,
|
||||||
]}
|
]}
|
||||||
onReset={(value) => {
|
onReset={(value) => {
|
||||||
router.query = { ...value, current: 1, size: 10 };
|
const deptId =
|
||||||
|
selectedDeptId && selectedDeptId !== ALL_DEPT_KEY
|
||||||
|
? asId(selectedDeptId)
|
||||||
|
: undefined;
|
||||||
|
router.query = { ...value, deptId, current: 1, size: 10 };
|
||||||
handleSearch();
|
handleSearch();
|
||||||
}}
|
}}
|
||||||
onFinish={(value) => {
|
onFinish={(value) => {
|
||||||
router.query = { ...value, current: 1, size: 10 };
|
const deptId =
|
||||||
|
selectedDeptId && selectedDeptId !== ALL_DEPT_KEY
|
||||||
|
? asId(selectedDeptId)
|
||||||
|
: undefined;
|
||||||
|
router.query = { ...value, deptId, current: 1, size: 10 };
|
||||||
handleSearch();
|
handleSearch();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -320,6 +417,8 @@ function PersonnelInfoPage(props) {
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{formModalOpen && (
|
{formModalOpen && (
|
||||||
<StaffFormModal
|
<StaffFormModal
|
||||||
|
|
@ -337,7 +436,7 @@ function PersonnelInfoPage(props) {
|
||||||
setFormModalOpen(false);
|
setFormModalOpen(false);
|
||||||
setCurrentId("");
|
setCurrentId("");
|
||||||
}}
|
}}
|
||||||
onSuccess={handleSearch}
|
onSuccess={refreshListAndCount}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{viewModalOpen && (
|
{viewModalOpen && (
|
||||||
|
|
@ -364,7 +463,7 @@ function PersonnelInfoPage(props) {
|
||||||
<StaffImportModal
|
<StaffImportModal
|
||||||
open={importModalOpen}
|
open={importModalOpen}
|
||||||
onCancel={() => setImportModalOpen(false)}
|
onCancel={() => setImportModalOpen(false)}
|
||||||
onSuccess={handleSearch}
|
onSuccess={refreshListAndCount}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{resetPasswordModalOpen && (
|
{resetPasswordModalOpen && (
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,28 @@
|
||||||
|
.personnel-info-layout {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
min-height: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personnel-info-tree {
|
||||||
|
width: 260px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-right: 1px solid #f0f0f0;
|
||||||
|
padding-right: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.personnel-info-main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.register-engineer-section {
|
.register-engineer-section {
|
||||||
border: 1px dashed #d9d9d9;
|
border: 1px dashed #d9d9d9;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue