'use client'; import * as React from 'react'; import { useState } from 'react'; import { Button } from '@nice/ui/components/button'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@nice/ui/components/dialog'; import { Input } from '@nice/ui/components/input'; import { Label } from '@nice/ui/components/label'; import { Textarea } from '@nice/ui/components/textarea'; import { DeptSelect, SingleDeptSelector, TermSelect } from '@/components/selector'; import { useTRPC } from '@fenghuo/client'; import { useQuery } from '@tanstack/react-query'; import { TaxonomySlug } from '@fenghuo/common'; import type { OrganizationDialogState } from './types.js'; interface OrganizationDialogProps { dialog: OrganizationDialogState; onClose: () => void; onSave: (data: { name: string; slug: string; description: string; parentId?: string; organizationTypeId?: string; professionIds?: string[]; }) => void; } export function OrganizationDialog({ dialog, onClose, onSave }: OrganizationDialogProps) { // 修改:统一使用 organization 字段而不是 department const [name, setName] = useState(dialog.organization?.name || ''); const [slug, setSlug] = useState(dialog.organization?.slug || ''); const [description, setDescription] = useState(dialog.organization?.description || ''); const [parentId, setParentId] = useState(dialog.parentId || dialog.organization?.parentId || ''); const [organizationTypeId, setOrganizationTypeId] = useState(''); const [professionIds, setProfessionIds] = useState([]); const trpc = useTRPC(); // 获取组织树数据用于显示父部门名称 const { data: organizationTree = [] } = useQuery({ ...trpc.organization.getTree.queryOptions({ includeInactive: false, }), }); // 重置表单数据 React.useEffect(() => { if (dialog.open) { // 修改:统一使用 organization 字段 setName(dialog.organization?.name || ''); setSlug(dialog.organization?.slug || ''); setDescription(dialog.organization?.description || ''); setParentId(dialog.parentId || dialog.organization?.parentId || ''); // 从部门的关联术语中提取数据 if (dialog.organization?.terms) { // 过滤组织类型术语 const orgTypeTerms = dialog.organization.terms.filter( (term) => term.taxonomy?.slug === TaxonomySlug.ORGANIZATION_TYPE, ); setOrganizationTypeId(orgTypeTerms[0]?.id || ''); // 过滤专业术语 const professionTerms = dialog.organization.terms.filter( (term) => term.taxonomy?.slug === TaxonomySlug.PROFESSION, ); setProfessionIds(professionTerms.map((term) => term.id)); } else { // 重置术语选择器 setOrganizationTypeId(''); setProfessionIds([]); } } }, [dialog]); const handleSave = () => { if (!name.trim()) return; onSave({ name: name.trim(), slug: slug.trim(), description: description.trim(), parentId: parentId || undefined, organizationTypeId: organizationTypeId || undefined, professionIds: professionIds.length > 0 ? professionIds : undefined, }); onClose(); }; const getDialogTitle = () => { switch (dialog.mode) { case 'edit': return '编辑部门'; case 'addChild': return '添加下级部门'; case 'add': default: return '新增部门'; } }; // 获取需要排除的部门ID(编辑模式下排除自身及其子部门) const getExcludeIds = React.useMemo(() => { if (dialog.mode === 'edit' && dialog.organization) { // 递归收集所有子部门ID const collectChildIds = (orgs: any[], parentId: string): string[] => { const childIds: string[] = []; orgs.forEach((org) => { if (org.parentId === parentId) { childIds.push(org.id); // 递归收集子部门的子部门 if (org.children) { childIds.push(...collectChildIds(org.children, org.id)); } else { // 如果没有children,从扁平化结构中查找 childIds.push(...collectChildIds(orgs, org.id)); } } }); return childIds; }; // 扁平化组织树 const flattenOrgs = (orgs: any[]): any[] => { return orgs.reduce((acc, org) => { acc.push(org); if (org.children) { acc.push(...flattenOrgs(org.children)); } return acc; }, []); }; const flatOrgs = flattenOrgs(organizationTree); const excludeIds = [dialog.organization.id, ...collectChildIds(flatOrgs, dialog.organization.id)]; return excludeIds; } return []; }, [dialog.mode, dialog.organization, organizationTree]); return ( !open && onClose()}> {getDialogTitle()}
setName(e.target.value)} required className="focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />
setSlug(e.target.value)} className="focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />