diff --git a/jjb.config.js b/jjb.config.js index 0a97931..869cda1 100644 --- a/jjb.config.js +++ b/jjb.config.js @@ -10,10 +10,10 @@ module.exports = { javaGitBranch: "dev", // 本地联调 safetyEval-service(context-path: /safetyEval,默认端口 8095) // 可通过环境变量覆盖: SAFETY_EVAL_API_HOST=http://192.168.x.x:8095 - // API_HOST: "https://gbs-gateway.qhdsafety.com", + API_HOST: "http://localhost:80", // API_HOST: "http://192.168.0.134", - API_HOST: "http://192.168.0.150", //太浅 + // API_HOST: "http://192.168.0.150", //太浅 // API_HOST: "http://192.168.0.152", //API_HOST: "http://192.168.0.103", //huwei }, diff --git a/src/api/staffInfo/index.js b/src/api/staffInfo/index.js index b23ea7c..06c0df3 100644 --- a/src/api/staffInfo/index.js +++ b/src/api/staffInfo/index.js @@ -8,6 +8,12 @@ export const staffInfoList = declareRequest( "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( "staffInfoLoading", "Get > /safetyEval/org-personnel/get", diff --git a/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js b/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js index b3000ea..daec355 100644 --- a/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js +++ b/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js @@ -12,13 +12,14 @@ import { Select, Space, Table, + Tree, TreeSelect, Upload, Tag, } from "antd"; import { InboxOutlined } from "@ant-design/icons"; 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 PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; 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 StaffViewModal from "~/components/StaffViewModal"; import StaffResumeModal from "~/components/StaffResumeModal"; +import { asId } from "~/utils/enterpriseInfo/idUtil"; import "./index.less"; const { router } = tools; const API_HOST = window.process?.env?.app?.API_HOST || ""; const evalCertLevelMap = CERT_LEVEL_LABEL_BY_TYPE.evaluator; +const ALL_DEPT_KEY = "ALL"; function PersonnelInfoPage(props) { const [formModalOpen, setFormModalOpen] = useState(false); @@ -65,6 +68,11 @@ function PersonnelInfoPage(props) { const [searchForm] = Form.useForm(); const [deptOptions, setDeptOptions] = 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 { orgDepartmentTreeData } = orgDepartment; const { @@ -73,8 +81,36 @@ function PersonnelInfoPage(props) { staffInfoLoading: loading, } = 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(() => { - orgDepartmentTree(); + loadTree(); + loadDeptPersonCount(); let cancelled = false; (async () => { try { @@ -100,20 +136,62 @@ function PersonnelInfoPage(props) { }, []); useEffect(() => { - searchForm.setFieldsValue(router.query); + const { deptId: _ignored, ...rest } = router.query || {}; + searchForm.setFieldsValue(rest); 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) => { - props.history.push( - `Certificate?staffId=${id}&staffName=${encodeURIComponent(staffName || "")}`, - ); + const refreshListAndCount = () => { + handleSearch(); + 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) => { Modal.confirm({ title: "提示", @@ -124,7 +202,7 @@ function PersonnelInfoPage(props) { const res = await props.staffInfoRemove({ id }); if (res?.success !== false) { message.success("删除成功"); - handleSearch(); + refreshListAndCount(); } }, }); @@ -160,166 +238,187 @@ function PersonnelInfoPage(props) { } > - - - , - - - , - - - {positionOptions.map((p) => ( - - {p.label} - - ))} - - , - ]} - onReset={(value) => { - router.query = { ...value, current: 1, size: 10 }; - handleSearch(); - }} - onFinish={(value) => { - router.query = { ...value, current: 1, size: 10 }; - handleSearch(); - }} - /> - +
+
组织机构
+ { + 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); + }} + /> +
+
+ + + , + + + {positionOptions.map((p) => ( + + {p.label} + + ))} + + , + ]} + onReset={(value) => { + const deptId = + selectedDeptId && selectedDeptId !== ALL_DEPT_KEY + ? asId(selectedDeptId) + : undefined; + router.query = { ...value, deptId, current: 1, size: 10 }; + handleSearch(); + }} + onFinish={(value) => { + const deptId = + selectedDeptId && selectedDeptId !== ALL_DEPT_KEY + ? asId(selectedDeptId) + : undefined; + router.query = { ...value, deptId, current: 1, size: 10 }; + handleSearch(); + }} + /> +
{ - if (!Array.isArray(list) || !list.length) return "-"; - return list - .map((item) => CAPABILITY_MAP[item.professionalCapabilityCode]) - .join("、"); - }, - }, - { - title: "证照名称", - dataIndex: "personnelCertFilingPageCOList", - width: 280, - render: (_, record) => { - const arr = []; - if(record.registeredSafetyEngineerCert){ - arr.push({TITLE_LEVEL_MAP[record.registeredSafetyEngineerCert.certLevel]}注册安全工程师); - } - if(record.evaluatorCert){ - arr.push({evalCertLevelMap[record.evaluatorCert.certLevel]}注册安全评价师); - } - return {arr}; + { + title: "专业能力", + dataIndex: "capabilityAssessment", + render: (list) => { + if (!Array.isArray(list) || !list.length) return "-"; + return list + .map((item) => CAPABILITY_MAP[item.professionalCapabilityCode]) + .join("、"); + }, }, - }, - { - title: "操作", - width: 200, - fixed: "right", - render: (_, record) => ( - - - - + { + title: "证照名称", + dataIndex: "personnelCertFilingPageCOList", + width: 280, + render: (_, record) => { + const arr = []; + if(record.registeredSafetyEngineerCert){ + arr.push({TITLE_LEVEL_MAP[record.registeredSafetyEngineerCert.certLevel]}注册安全工程师); + } + if(record.evaluatorCert){ + arr.push({evalCertLevelMap[record.evaluatorCert.certLevel]}注册安全评价师); + } + return {arr}; + }, + }, + { + title: "操作", + width: 200, + fixed: "right", + render: (_, record) => ( + + + + - - - - ), - }, - ]} - dataSource={dataSource} - scroll={{ y: props.scrollY, x: 1300 }} - 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(); - }, - }} - /> + + + + ), + }, + ]} + dataSource={dataSource} + scroll={{ y: props.scrollY, x: 1300 }} + 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 && ( )} {viewModalOpen && ( @@ -364,7 +463,7 @@ function PersonnelInfoPage(props) { setImportModalOpen(false)} - onSuccess={handleSearch} + onSuccess={refreshListAndCount} /> )} {resetPasswordModalOpen && ( diff --git a/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.less b/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.less index 1fe6988..ba25eee 100644 --- a/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.less +++ b/src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.less @@ -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 { border: 1px dashed #d9d9d9; border-radius: 8px;