import { useEffect, useState, useCallback } from 'react'; import { Tree, Button, Space, Modal, Form, Input, InputNumber, Table, Popconfirm, message, Empty, Tag, theme, } from 'antd'; import { PlusOutlined, DeleteOutlined, EditOutlined, ApartmentOutlined, } from '@ant-design/icons'; import type { DataNode } from 'antd/es/tree'; import { listOrgTree, createOrg, updateOrg, deleteOrg, listDeptTree, createDept, deleteDept, listPositions, createPosition, deletePosition, type OrganizationInfo, type DepartmentInfo, type PositionInfo, } from '../api/orgs'; export default function Organizations() { const { token } = theme.useToken(); const isDark = token.colorBgContainer === '#111827' || token.colorBgContainer === 'rgb(17, 24, 39)'; const cardStyle = { background: isDark ? '#111827' : '#FFFFFF', borderRadius: 12, border: `1px solid ${isDark ? '#1E293B' : '#F1F5F9'}`, }; // --- Org tree state --- const [orgTree, setOrgTree] = useState([]); const [selectedOrg, setSelectedOrg] = useState(null); const [, setLoading] = useState(false); // --- Department tree state --- const [deptTree, setDeptTree] = useState([]); const [selectedDept, setSelectedDept] = useState(null); // --- Position list state --- const [positions, setPositions] = useState([]); // --- Modal state --- const [orgModalOpen, setOrgModalOpen] = useState(false); const [deptModalOpen, setDeptModalOpen] = useState(false); const [positionModalOpen, setPositionModalOpen] = useState(false); const [editOrg, setEditOrg] = useState(null); const [orgForm] = Form.useForm(); const [deptForm] = Form.useForm(); const [positionForm] = Form.useForm(); // --- Fetch org tree --- const fetchOrgTree = useCallback(async () => { setLoading(true); try { const tree = await listOrgTree(); setOrgTree(tree); if (selectedOrg) { const stillExists = findOrgInTree(tree, selectedOrg.id); if (!stillExists) { setSelectedOrg(null); setDeptTree([]); setPositions([]); } } } catch { message.error('加载组织树失败'); } setLoading(false); }, [selectedOrg]); useEffect(() => { fetchOrgTree(); }, [fetchOrgTree]); // --- Fetch dept tree when org selected --- const fetchDeptTree = useCallback(async () => { if (!selectedOrg) return; try { const tree = await listDeptTree(selectedOrg.id); setDeptTree(tree); if (selectedDept) { const stillExists = findDeptInTree(tree, selectedDept.id); if (!stillExists) { setSelectedDept(null); setPositions([]); } } } catch { message.error('加载部门树失败'); } }, [selectedOrg, selectedDept]); useEffect(() => { fetchDeptTree(); }, [fetchDeptTree]); // --- Fetch positions when dept selected --- const fetchPositions = useCallback(async () => { if (!selectedDept) return; try { const list = await listPositions(selectedDept.id); setPositions(list); } catch { message.error('加载岗位列表失败'); } }, [selectedDept]); useEffect(() => { fetchPositions(); }, [fetchPositions]); // --- Org handlers --- const handleCreateOrg = async (values: { name: string; code?: string; sort_order?: number; }) => { try { if (editOrg) { await updateOrg(editOrg.id, { name: values.name, code: values.code, sort_order: values.sort_order, version: editOrg.version, }); message.success('组织更新成功'); } else { await createOrg({ name: values.name, code: values.code, parent_id: selectedOrg?.id, sort_order: values.sort_order, }); message.success('组织创建成功'); } setOrgModalOpen(false); setEditOrg(null); orgForm.resetFields(); fetchOrgTree(); } catch (err: unknown) { const errorMsg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message || '操作失败'; message.error(errorMsg); } }; const handleDeleteOrg = async (id: string) => { try { await deleteOrg(id); message.success('组织已删除'); setSelectedOrg(null); setDeptTree([]); setPositions([]); fetchOrgTree(); } catch (err: unknown) { const errorMsg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message || '删除失败'; message.error(errorMsg); } }; // --- Dept handlers --- const handleCreateDept = async (values: { name: string; code?: string; sort_order?: number; }) => { if (!selectedOrg) return; try { await createDept(selectedOrg.id, { name: values.name, code: values.code, parent_id: selectedDept?.id, sort_order: values.sort_order, }); message.success('部门创建成功'); setDeptModalOpen(false); deptForm.resetFields(); fetchDeptTree(); } catch (err: unknown) { const errorMsg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message || '操作失败'; message.error(errorMsg); } }; const handleDeleteDept = async (id: string) => { try { await deleteDept(id); message.success('部门已删除'); setSelectedDept(null); setPositions([]); fetchDeptTree(); } catch (err: unknown) { const errorMsg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message || '删除失败'; message.error(errorMsg); } }; // --- Position handlers --- const handleCreatePosition = async (values: { name: string; code?: string; level?: number; sort_order?: number; }) => { if (!selectedDept) return; try { await createPosition(selectedDept.id, { name: values.name, code: values.code, level: values.level, sort_order: values.sort_order, }); message.success('岗位创建成功'); setPositionModalOpen(false); positionForm.resetFields(); fetchPositions(); } catch (err: unknown) { const errorMsg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message || '操作失败'; message.error(errorMsg); } }; const handleDeletePosition = async (id: string) => { try { await deletePosition(id); message.success('岗位已删除'); fetchPositions(); } catch { message.error('删除失败'); } }; // --- Tree node converters --- const convertOrgTree = (items: OrganizationInfo[]): DataNode[] => items.map((item) => ({ key: item.id, title: ( {item.name}{' '} {item.code && {item.code}} ), children: convertOrgTree(item.children), })); const convertDeptTree = (items: DepartmentInfo[]): DataNode[] => items.map((item) => ({ key: item.id, title: ( {item.name}{' '} {item.code && {item.code}} ), children: convertDeptTree(item.children), })); const onSelectOrg = (selectedKeys: React.Key[]) => { if (selectedKeys.length === 0) { setSelectedOrg(null); setDeptTree([]); setSelectedDept(null); setPositions([]); return; } const org = findOrgInTree(orgTree, selectedKeys[0] as string); setSelectedOrg(org); setSelectedDept(null); setPositions([]); }; const onSelectDept = (selectedKeys: React.Key[]) => { if (selectedKeys.length === 0) { setSelectedDept(null); setPositions([]); return; } const dept = findDeptInTree(deptTree, selectedKeys[0] as string); setSelectedDept(dept); }; const positionColumns = [ { title: '岗位名称', dataIndex: 'name', key: 'name' }, { title: '编码', dataIndex: 'code', key: 'code', render: (v?: string) => v || '-' }, { title: '级别', dataIndex: 'level', key: 'level' }, { title: '排序', dataIndex: 'sort_order', key: 'sort_order' }, { title: '操作', key: 'actions', render: (_: unknown, record: PositionInfo) => ( handleDeletePosition(record.id)} > ), }, ]; return (
{/* 页面标题 */}

组织架构管理

管理组织、部门和岗位的层级结构
{/* 三栏布局 */}
{/* 左栏:组织树 */}
组织
{orgTree.length > 0 ? ( ) : ( )}
{/* 中栏:部门树 */}
{selectedOrg ? `${selectedOrg.name} · 部门` : '部门'} {selectedOrg && (
{selectedOrg ? ( deptTree.length > 0 ? ( ) : ( ) ) : ( )}
{/* 右栏:岗位表 */}
{selectedDept ? `${selectedDept.name} · 岗位` : '岗位'} {selectedDept && ( )}
{selectedDept ? ( ) : (
)} {/* Org Modal */} { setOrgModalOpen(false); setEditOrg(null); }} onOk={() => orgForm.submit()} >
{/* Dept Modal */} setDeptModalOpen(false)} onOk={() => deptForm.submit()} >
{/* Position Modal */} setPositionModalOpen(false)} onOk={() => positionForm.submit()} >
); } // --- Helpers --- function findOrgInTree( tree: OrganizationInfo[], id: string, ): OrganizationInfo | null { for (const item of tree) { if (item.id === id) return item; const found = findOrgInTree(item.children, id); if (found) return found; } return null; } function findDeptInTree( tree: DepartmentInfo[], id: string, ): DepartmentInfo | null { for (const item of tree) { if (item.id === id) return item; const found = findDeptInTree(item.children, id); if (found) return found; } return null; }