import { PlusOutlined } from "@ant-design/icons"; import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Button, Empty, Form, Input, message, Modal, Select, Space, Tree, Table, TreeSelect, } from "antd"; import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd"; import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction"; import { useEffect, useMemo, useRef, useState } from "react"; import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout"; import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm"; import { NS_ORG_DEPARTMENT, NS_ORG_POSITION, NS_STAFF_INFO, } from "~/enumerate/namespace"; import { asId, sameId } from "~/utils/enterpriseInfo/idUtil"; function findDeptNode(nodes = [], id) { for (const node of nodes) { if (sameId(node.id, id)) { return node; } if (node.children?.length) { const found = findDeptNode(node.children, id); if (found) { return found; } } } return null; } function collectSelfAndDescendantIds(node) { if (!node) { return []; } const ids = [asId(node.id)].filter(Boolean); (node.children || []).forEach((child) => { ids.push(...collectSelfAndDescendantIds(child)); }); return ids; } function toSelectedDept(node) { if (!node) { return null; } return { id: asId(node.id), deptName: node.deptName || node.title, managerAccount: node.managerAccount, managerName: node.managerName, deptLevelName: node.deptLevelName, }; } function DepartmentPositionPage(props) { const { orgPosition } = props; const { orgPositionLoading } = orgPosition; const [selectedDept, setSelectedDept] = useState(null); const [treeData, setTreeData] = useState([]); const [deptModalOpen, setDeptModalOpen] = useState(false); const [positionModalOpen, setPositionModalOpen] = useState(false); const [assignRoleModalOpen, setAssignRoleModalOpen] = useState(false); const [assignRoleRecord, setAssignRoleRecord] = useState(null); const [gbsRoleOptions, setGbsRoleOptions] = useState([]); const [gbsRoleLoading, setGbsRoleLoading] = useState(false); const [currentId, setCurrentId] = useState(""); const [filterKeyword, setFilterKeyword] = useState(""); const [staffOptions, setStaffOptions] = useState([]); const [positionForm] = Form.useForm(); const [modalForm] = Form.useForm(); const [assignRoleForm] = Form.useForm(); const [dataSource, setDataSource] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [pageIndex, setPageIndex] = useState(1); const [pageSize, setPageSize] = useState(10); useEffect(() => { (async () => { try { const res = await props.staffInfoList?.({ pageIndex: 1, pageSize: 500, }); setStaffOptions( (res?.data || []).map((s) => ({ label: `${s.userName ?? s.staffName}(${s.account})`, value: s.account, staffName: s.userName ?? s.staffName, })), ); } catch (err) { console.warn("[DepartmentPosition] load staff options failed:", err); } })(); }, []); const fetchPositionData = async (page = 1, size = 10) => { if (!selectedDept?.id) return; setLoading(true); try { const formData = positionForm.getFieldsValue(); const res = await props.orgPositionList({ ...formData, pageIndex: page, pageSize: size, deptId: asId(selectedDept.id), }); setDataSource(res?.data || []); setTotal(res?.totalCount || 0); } catch { setDataSource([]); setTotal(0); } finally { setLoading(false); } }; const loadTree = async () => { try { const res = await props.orgDepartmentTree(); const tree = res?.data || []; setTreeData(tree); if (tree.length) { const findById = (nodes, id) => { for (const n of nodes) { if (n.id === id) return n; if (n.children?.length) { const found = findById(n.children, id); if (found) return found; } } return null; }; const matched = selectedDept?.id && findById(tree, selectedDept.id); console.log(matched); setSelectedDept(matched || tree[0]); } } catch (err) { console.warn("[DepartmentPosition] loadTree failed:", err); setTreeData([]); } }; useEffect(() => { loadTree(); }, []); useEffect(() => { if (selectedDept?.id) { fetchPositionData(1, 10); } }, [selectedDept?.id]); const filteredTree = useMemo(() => { if (!filterKeyword) { return treeData; } const filterNodes = (nodes) => { return nodes .map((node) => { const children = node.children ? filterNodes(node.children) : []; const match = (node.deptName || node.title)?.includes(filterKeyword); if (match || children.length) { return { ...node, children }; } return null; }) .filter(Boolean); }; return filterNodes(treeData); }, [treeData, filterKeyword]); const onDeleteDept = (id) => { Modal.confirm({ title: "提示", content: "数据将会删除,您是否确认删除?", okText: "是", cancelText: "否", onOk: async () => { try { const node = findDeptNode(treeData, id); const deptIds = collectSelfAndDescendantIds(node); const countRes = await props.staffInfoDeptPersonCount?.(); const countMap = {}; (countRes?.data || []).forEach((item) => { const deptId = asId(item.deptId); if (deptId) { countMap[deptId] = Number(item.personCount) || 0; } }); const personCount = deptIds.reduce( (sum, deptId) => sum + (countMap[deptId] || 0), 0, ); if (personCount > 0) { message.warning("该部门(含子部门)下存在人员,无法删除"); return; } } catch (err) { console.warn("[DepartmentPosition] check dept personnel failed:", err); message.warning("校验部门人员失败,请稍后重试"); return; } const res = await props.orgDepartmentRemove({ data: id }); if (res?.success !== false) { message.success("删除成功"); if (sameId(selectedDept?.id, id)) { setSelectedDept(null); } loadTree(); } }, }); }; const onDeletePosition = (id) => { Modal.confirm({ title: "提示", content: "数据将会删除,您是否确认删除?", okText: "是", cancelText: "否", onOk: async () => { try { const res = await props.staffInfoList?.({ postId: asId(id), current: 1, size: 1, }); const total = Number(res?.total ?? res?.totalCount ?? 0); if (total > 0) { message.warning("该岗位下存在人员,无法删除"); return; } } catch (err) { console.warn("[DepartmentPosition] check position personnel failed:", err); message.warning("校验岗位人员失败,请稍后重试"); return; } const res = await props.orgPositionRemove({ data: id }); if (res?.success !== false) { message.success("删除成功"); fetchPositionData(1, 10); } }, }); }; const closeDeptModal = () => { modalForm.resetFields(); setCurrentId(""); setDeptModalOpen(false); }; const closePositionModal = () => { modalForm.resetFields(); setCurrentId(""); setPositionModalOpen(false); }; const closeAssignRoleModal = () => { assignRoleForm.resetFields(); setAssignRoleRecord(null); setAssignRoleModalOpen(false); }; const openAssignRoleModal = async (record) => { setAssignRoleRecord(record); assignRoleForm.resetFields(); assignRoleForm.setFieldsValue({ gbsRoleCode: record?.gbsRoleCode || undefined, }); setAssignRoleModalOpen(true); setGbsRoleLoading(true); try { const res = await props.orgPositionGbsRoles?.(); const list = res?.data || []; setGbsRoleOptions( list.map((item) => ({ label: item.roleName ? `${item.roleName}(${item.roleCode})` : item.roleCode, value: item.roleCode, roleId: item.roleId, roleName: item.roleName, roleCode: item.roleCode, })), ); } catch (err) { console.warn("[DepartmentPosition] load gbs roles failed:", err); setGbsRoleOptions([]); } finally { setGbsRoleLoading(false); } }; const submitAssignRole = async (values) => { if (!assignRoleRecord?.id) { return; } const matched = gbsRoleOptions.find( (item) => item.value === values.gbsRoleCode, ); if (!matched?.roleId) { message.warning("请选择有效的GBS角色(需包含角色ID)"); return; } const res = await props.orgPositionAssignGbsRole({ id: assignRoleRecord.id, gbsRoleId: matched.roleId, gbsRoleCode: values.gbsRoleCode, gbsRoleName: matched?.roleName, }); if (res?.success !== false) { message.success("分配角色成功"); closeAssignRoleModal(); fetchPositionData(pageIndex, pageSize); } }; const openDeptModal = (record) => { setCurrentId(asId(record?.id) || ""); setDeptModalOpen(true); }; const openPositionModal = (record) => { if (!selectedDept?.id) { message.warning("请先选择部门"); return; } setCurrentId(asId(record?.id) || ""); modalForm.resetFields(); if (record) { modalForm.setFieldsValue({ ...record, deptId: asId(record.deptId || selectedDept.id), deptName: record.deptName || selectedDept.deptName, }); } else { modalForm.setFieldsValue({ deptId: asId(selectedDept.id), deptName: selectedDept.deptName, }); } setPositionModalOpen(true); }; const submitPosition = async (values) => { const request = currentId ? props.orgPositionEdit : props.orgPositionAdd; const payload = { ...values }; if (currentId) { payload.id = currentId; } payload.deptId = asId(values.deptId || selectedDept?.id); const res = await request(payload); if (res?.success !== false) { message.success(currentId ? "编辑成功" : "添加成功"); closePositionModal(); fetchPositionData(pageIndex, pageSize); } else { } }; return (
setFilterKeyword(e.target.value)} /> { setSelectedDept(node); }} />
{selectedDept?.id ? ( <>
当前部门: {selectedDept.deptName}
, ]} onFinish={() => fetchPositionData(1, 10)} onReset={() => fetchPositionData(1, 10)} style={{ marginBottom: 16 }} /> ( )} columns={[ { title: "名称", dataIndex: "positionName" }, { title: "职责", dataIndex: "dutyDesc" }, { title: "GBS角色", dataIndex: "gbsRoleName", render: (text, record) => text || record.gbsRoleCode || "-", }, { title: "备注", dataIndex: "remark" }, { title: "操作", width: 220, render: (_, record) => ( ), }, ]} dataSource={dataSource} loading={loading} pagination={{ current: pageIndex, pageSize, total, showSizeChanger: true, showTotal: (t) => `共 ${t} 条`, }} scroll={{ y: props.scrollY }} onChange={(pag) => { setPageIndex(pag.current); setPageSize(pag.pageSize); fetchPositionData(pag.current, pag.pageSize); }} /> ) : ( )} {deptModalOpen && ( )} {positionModalOpen && (
)} {assignRoleModalOpen && (
); } export default Connect( [NS_ORG_DEPARTMENT, NS_ORG_POSITION, NS_STAFF_INFO], true, )(AntdTableFuncControl(DepartmentPositionPage));