diff --git a/crop-x/src/app/(app)/central-config/user/department/components/DepartmentFormDialog.tsx b/crop-x/src/app/(app)/central-config/user/department/components/DepartmentFormDialog.tsx index 58c892c..6ba62dd 100644 --- a/crop-x/src/app/(app)/central-config/user/department/components/DepartmentFormDialog.tsx +++ b/crop-x/src/app/(app)/central-config/user/department/components/DepartmentFormDialog.tsx @@ -7,13 +7,20 @@ 'use client'; -import { useState } from 'react'; +import { useState, useCallback } from 'react'; +import { toast } from 'sonner'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; -import { Department } from '../types'; +import { Department, CreateDepartmentForm } from '../types'; +import { + createDepartment, + transformCreateDepartmentData, + debounce, + generateRandomOrderIndex, +} from './departmentCreateApi'; interface DepartmentFormDialogProps { open: boolean; @@ -21,6 +28,7 @@ interface DepartmentFormDialogProps { editingDepartment: Department | null; parentDepartment: Department | null; onSave: (formData: Partial) => void; + refreshDepartmentTree?: () => void; } export function DepartmentFormDialog({ @@ -28,13 +36,20 @@ export function DepartmentFormDialog({ onOpenChange, editingDepartment, parentDepartment, - onSave + onSave, + refreshDepartmentTree, }: DepartmentFormDialogProps) { - const [formData, setFormData] = useState>({ + const [formData, setFormData] = useState({ + name: '', + code: '', + manager: '', + phone: '', + email: '', + description: '', status: 'active', - sort: 0, + sort: generateRandomOrderIndex(), + parentId: parentDepartment?.id || '', level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1, - parentId: parentDepartment?.id, }); const [loading, setLoading] = useState(false); @@ -43,42 +58,125 @@ export function DepartmentFormDialog({ useState(() => { if (editingDepartment) { setFormData({ - ...editingDepartment, - children: undefined, // 排除children字段 + name: editingDepartment.name, + code: editingDepartment.code, + manager: editingDepartment.manager || '', + phone: editingDepartment.phone || '', + email: editingDepartment.email || '', + description: editingDepartment.description || '', + status: editingDepartment.status, + sort: editingDepartment.sort, + parentId: editingDepartment.parentId || '', + level: editingDepartment.level, }); } else { setFormData({ - parentId: parentDepartment?.id, - level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1, + name: '', + code: '', + manager: '', + phone: '', + email: '', + description: '', status: 'active', - sort: 0, + sort: generateRandomOrderIndex(), + parentId: parentDepartment?.id || '', + level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1, }); } }); - const handleInputChange = (field: keyof Department, value: string | number) => { + const handleInputChange = (field: keyof CreateDepartmentForm, value: string | number) => { setFormData(prev => ({ ...prev, [field]: value })); }; + // 表单验证 + const validateForm = (): boolean => { + if (!formData.name?.trim()) { + toast.error('请输入部门名称'); + return false; + } + if (!formData.code?.trim()) { + toast.error('请输入部门编码'); + return false; + } + if (formData.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { + toast.error('邮箱格式不正确'); + return false; + } + return true; + }; + + // 创建部门的API调用函数(带防抖) + const createDepartmentWithDebounce = useCallback( + debounce(async (form: CreateDepartmentForm) => { + try { + setLoading(true); + + // 调用API创建部门 + await createDepartment(form); + + // 成功处理 + toast.success('创建一级部门成功'); + + // 关闭对话框 + onOpenChange(false); + + // 刷新部门树 + if (refreshDepartmentTree) { + refreshDepartmentTree(); + } + + // 重置表单 + setFormData({ + name: '', + code: '', + manager: '', + phone: '', + email: '', + description: '', + status: 'active', + sort: generateRandomOrderIndex(), + parentId: parentDepartment?.id || '', + level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1, + }); + + } catch (error) { + // 失败处理 - 不关闭页面 + console.error('创建部门失败:', error); + const errorMessage = error instanceof Error ? error.message : '创建一级部门失败'; + toast.error(errorMessage); + } finally { + setLoading(false); + } + }, 1000), // 1秒防抖 + [onOpenChange, refreshDepartmentTree, parentDepartment] + ); + const handleSubmit = async () => { - if (!formData.name || !formData.code) { + // 表单验证 + if (!validateForm()) { return; } - setLoading(true); - try { - await onSave(formData); - // 重置表单 - setFormData({ - status: 'active', - sort: 0, - level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1, - parentId: parentDepartment?.id, - }); - } catch (error) { - console.error('Failed to save department:', error); - } finally { - setLoading(false); + // 如果是编辑模式,使用原有的onSave逻辑 + if (editingDepartment) { + setLoading(true); + try { + await onSave(formData); + toast.success('部门更新成功'); + onOpenChange(false); + if (refreshDepartmentTree) { + refreshDepartmentTree(); + } + } catch (error) { + console.error('Failed to update department:', error); + toast.error('部门更新失败'); + } finally { + setLoading(false); + } + } else { + // 创建模式,使用API调用(带防抖) + createDepartmentWithDebounce(formData); } }; @@ -87,10 +185,16 @@ export function DepartmentFormDialog({ onOpenChange(false); // 重置表单 setFormData({ + name: '', + code: '', + manager: '', + phone: '', + email: '', + description: '', status: 'active', - sort: 0, + sort: generateRandomOrderIndex(), + parentId: parentDepartment?.id || '', level: parentDepartment ? (parentDepartment.level || 1) + 1 : 1, - parentId: parentDepartment?.id, }); } }; diff --git a/crop-x/src/app/(app)/central-config/user/department/components/DepartmentHeader.tsx b/crop-x/src/app/(app)/central-config/user/department/components/DepartmentHeader.tsx index 62aabfd..e168a18 100644 --- a/crop-x/src/app/(app)/central-config/user/department/components/DepartmentHeader.tsx +++ b/crop-x/src/app/(app)/central-config/user/department/components/DepartmentHeader.tsx @@ -9,10 +9,12 @@ import { Card } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; -import { Building2, Plus } from 'lucide-react'; +import { Building2, Plus, RefreshCw } from 'lucide-react'; interface DepartmentHeaderProps { onAdd: () => void; + onRefresh?: () => void; + loading?: boolean; } export function DepartmentHeader({ onAdd }: DepartmentHeaderProps) { diff --git a/crop-x/src/app/(app)/central-config/user/department/components/departmentApi.ts b/crop-x/src/app/(app)/central-config/user/department/components/departmentApi.ts new file mode 100644 index 0000000..174a359 --- /dev/null +++ b/crop-x/src/app/(app)/central-config/user/department/components/departmentApi.ts @@ -0,0 +1,323 @@ +/** + * filekorolheader: 部门管理API接口 - 部门树形数据查询接口服务 + * 功能:API请求封装、数据转换、错误处理、树形结构数据处理 + * 路径:/central-config/user/department/components/departmentApi + * 规范:遵循crop-x/docs/开发项目规范.md,使用SDK API调用,TypeScript类型安全 + */ + +import { getAuthToken } from "@/utils/token"; +import { + getDepartmentTreeApiV1DepartmentsTreeGet, + getUsersApiV1UsersGet +} from "@/lib/api/sdk.gen"; + +// API返回的部门数据类型 +export interface DepartmentApiData { + id: string; + name: string; + code: string; + parent_id: string | null; + manager_name: string | null; + manager_phone: string | null; + manager_email: string | null; + description: string | null; + status: string; + created_at: string; + updated_at: string; + tenant_id: string; + order_index: number; + created_by: string | null; + updated_by: string | null; + children?: DepartmentApiData[]; +} + +// API返回的用户数据类型(用于部门成员) +export interface UserApiData { + id: string; + username: string; + email: string; + full_name: string | null; + display_name: string | null; + phone: string | null; + is_active: boolean; + department_id: string | null; + department_name: string | null; + created_at: string; + last_login_at: string | null; +} + +// API响应接口 +export interface DepartmentTreeApiResponse { + data: DepartmentApiData[]; + total: number; + success: boolean; + message?: string; +} + +// 页面使用的部门数据类型(转换后的) +export interface Department { + id: string; + name: string; + code: string; + parentId: string | null; + managerId: string | null; + description: string | null; + isActive: boolean; + createdAt: string; + updatedAt: string; + tenantId: string; + sortOrder: number; + memberCount?: number; + children: Department[]; + expanded: boolean; + level: number; + // 兼容现有页面的字段 + manager?: string; + employeeCount?: number; + description?: string; + status?: 'active' | 'inactive'; +} + +// 页面使用的用户数据类型(转换后的) +export interface DepartmentUser { + id: string; + username: string; + email: string; + fullName: string | null; + displayName: string | null; + phone: string | null; + isActive: boolean; + departmentId: string | null; + departmentName: string | null; + createdAt: string; + lastLoginAt: string | null; +} + +// 查询参数接口 +export interface DepartmentQueryParams { + include_inactive?: boolean; + include_members?: boolean; +} + +/** + * 获取部门树形结构数据 + */ +export async function fetchDepartmentTree(params: DepartmentQueryParams = {}): Promise { + try { + // 构建查询参数对象 + const queryParams: any = {}; + + if (params.include_inactive !== undefined) queryParams.include_inactive = params.include_inactive; + if (params.include_members !== undefined) queryParams.include_members = params.include_members; + + // 获取认证token + const token = getAuthToken(); + console.log('部门管理API调用参数:', queryParams); + + // 使用SDK API调用部门树形结构查询接口 + const response = await getDepartmentTreeApiV1DepartmentsTreeGet({ + query: { + ...queryParams, + // 添加时间戳防止缓存 + _t: Date.now(), + }, + headers: token ? { + 'Authorization': `Bearer ${token}`, + } : undefined, + }); + + if (response.error) { + throw new Error(`API error: ${response.error.message || 'Unknown error'}`); + } + + const data = response.data as any; + console.log('部门管理API响应:', data); + + // 根据实际API响应格式处理数据 + if (Array.isArray(data)) { + // 如果API直接返回数组 + return { + data: data, + total: data.length, + success: true, + }; + } else if (data && typeof data === 'object') { + // 如果API返回对象格式 + return { + data: data.data || data.items || [], + total: data.total || data.count || 0, + success: data.success !== false, + message: data.message, + }; + } else { + // 其他情况,返回空结果 + return { + data: [], + total: 0, + success: false, + message: 'Invalid response format', + }; + } + } catch (error) { + console.error('Failed to fetch department tree:', error); + throw error; + } +} + +/** + * 获取部门成员列表 + */ +export async function fetchDepartmentUsers(departmentId: string): Promise { + try { + // 获取认证token + const token = getAuthToken(); + + // 使用SDK API调用用户查询接口,按部门筛选 + const response = await getUsersApiV1UsersGet({ + query: { + department_id: departmentId, + _t: Date.now(), + }, + headers: token ? { + 'Authorization': `Bearer ${token}`, + } : undefined, + }); + + if (response.error) { + throw new Error(`API error: ${response.error.message || 'Unknown error'}`); + } + + const data = response.data as any; + + let users: any[] = []; + if (Array.isArray(data)) { + users = data; + } else if (data && typeof data === 'object' && data.data) { + users = data.data; + } + + return users.map(transformUserData); + } catch (error) { + console.error('Failed to fetch department users:', error); + throw error; + } +} + +/** + * 将API数据转换为页面所需的部门数据格式 + */ +export function transformDepartmentData(department: DepartmentApiData, level: number = 0): Department { + return { + id: department.id, + name: department.name, + code: department.code, + parentId: department.parent_id, + managerId: null, // API中没有manager_id字段 + description: department.description, + isActive: department.status === 'active', + createdAt: formatDate(department.created_at), + updatedAt: formatDate(department.updated_at), + tenantId: department.tenant_id, + sortOrder: department.order_index, + memberCount: 0, // API中没有member_count字段 + children: (department.children || []).map(child => transformDepartmentData(child, level + 1)), + expanded: level === 0, // 默认展开顶级部门 + level, + // 兼容现有页面的字段 + manager: department.manager_name || undefined, + phone: department.manager_phone || undefined, + email: department.manager_email || undefined, + employeeCount: 0, + status: department.status as 'active' | 'inactive', + }; +} + +/** + * 将API数据转换为页面所需的用户数据格式 + */ +export function transformUserData(user: UserApiData): DepartmentUser { + return { + id: user.id, + username: user.username, + email: user.email, + fullName: user.full_name, + displayName: user.display_name || user.full_name || user.username, + phone: user.phone, + isActive: user.is_active, + departmentId: user.department_id, + departmentName: user.department_name, + createdAt: formatDate(user.created_at), + lastLoginAt: user.last_login_at ? formatDate(user.last_login_at) : null, + }; +} + +/** + * 批量转换部门数据 + */ +export function transformDepartmentList(departments: DepartmentApiData[]): Department[] { + return departments.map(dept => transformDepartmentData(dept)); +} + +/** + * 扁平化部门树形结构(用于搜索和筛选) + */ +export function flattenDepartments(departments: Department[]): Department[] { + const result: Department[] = []; + + function flatten(depts: Department[]) { + depts.forEach(dept => { + result.push(dept); + if (dept.children && dept.children.length > 0) { + flatten(dept.children); + } + }); + } + + flatten(departments); + return result; +} + +/** + * 在部门树中查找部门 + */ +export function findDepartmentInTree(departments: Department[], departmentId: string): Department | null { + for (const dept of departments) { + if (dept.id === departmentId) { + return dept; + } + if (dept.children && dept.children.length > 0) { + const found = findDepartmentInTree(dept.children, departmentId); + if (found) return found; + } + } + return null; +} + +/** + * 格式化日期 + */ +function formatDate(dateString: string): string { + try { + const date = new Date(dateString); + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }).replace(/\//g, '-'); + } catch (error) { + return dateString; + } +} + +// Department tree state interface for page components +export interface DepartmentTreeState { + departments: Department[]; + flattenedDepartments: Department[]; + selectedDepartment: Department | null; + loading: boolean; + error: string | null; + searchKeyword: string; + expandedDepartments: Set; +} \ No newline at end of file diff --git a/crop-x/src/app/(app)/central-config/user/department/components/departmentCreateApi.ts b/crop-x/src/app/(app)/central-config/user/department/components/departmentCreateApi.ts new file mode 100644 index 0000000..a2245f3 --- /dev/null +++ b/crop-x/src/app/(app)/central-config/user/department/components/departmentCreateApi.ts @@ -0,0 +1,130 @@ +/** + * filekorolheader: 部门管理API接口 - 部门数据CRUD操作接口服务 + * 功能:API请求封装、数据转换、错误处理、部门树形管理 + * 路径:/central-config/user/department/components/departmentApi + * 规范:遵循crop-x/docs/开发项目规范.md,使用SDK API调用,TypeScript类型安全 + */ + +import { getAuthToken } from "@/utils/token"; +import { + createDepartmentApiV1DepartmentsPost, +} from "@/lib/api/sdk.gen"; +import { + Department, + CreateDepartmentForm, +} from '../types'; + +/** + * API请求创建部门的数据结构(对应Python字段) + */ +export interface CreateDepartmentApiRequest { + code: string; // 部门编码 + name: string; // 部门名称 + description?: string; // 部门描述 + manager_name?: string; // 管理者名称 + manager_phone?: string; // 管理者电话 + manager_email?: string; // 管理者邮箱(必须符合邮箱规则) + parent_id: string; // 父级部门ID,一级部门为空字符串 + order_index: number; // 排序索引,0-10000的整数 + status: string; // 状态,默认"active"表示有效 +} + +/** + * API响应数据结构 + */ +export interface CreateDepartmentApiResponse { + code: string; + name: string; + description?: string; + manager_name?: string; + manager_phone?: string; + manager_email?: string; + parent_id: string; + order_index: number; + status: string; +} + +/** + * 将React表单数据(驼峰命名法)转换为API请求数据(Python蛇形命名法) + */ +export function transformCreateDepartmentData(formData: CreateDepartmentForm): CreateDepartmentApiRequest { + return { + code: formData.code, + name: formData.name, + description: formData.description || null, + manager_name: formData.manager || undefined, + manager_phone: formData.phone || undefined, + manager_email: formData.email || undefined, + parent_id: formData.parentId || "", + order_index: formData.sort || 0, + status: formData.status || "active", + }; +} + +/** + * 邮箱格式验证 + */ +export function isValidEmail(email: string): boolean { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); +} + +/** + * 创建部门API调用 + */ +export async function createDepartment(formData: CreateDepartmentForm): Promise { + try { + // 获取认证token + const token = getAuthToken(); + console.log('创建部门API调用参数:', formData); + + // 转换表单数据为API请求格式 + const apiRequestData = transformCreateDepartmentData(formData); + + // 邮箱格式验证 + if (apiRequestData.manager_email && !isValidEmail(apiRequestData.manager_email)) { + throw new Error('邮箱格式不正确'); + } + + // 使用真正的SDK API调用 + const response = await createDepartmentApiV1DepartmentsPost({ + body: apiRequestData, + headers: token ? { + 'Authorization': `Bearer ${token}`, + } : undefined, + }); + + if (response.error) { + throw new Error(`API error: ${response.error.message || 'Unknown error'}`); + } + + const data = response.data as CreateDepartmentApiResponse; + console.log('创建部门API响应:', data); + + return data; + } catch (error) { + console.error('Failed to create department:', error); + throw error; + } +} + +/** + * 生成随机排序索引(0-10000) + */ +export function generateRandomOrderIndex(): number { + return Math.floor(Math.random() * 10001); +} + +/** + * 防抖函数 + */ +export function debounce any>( + func: T, + delay: number +): (...args: Parameters) => void { + let timeoutId: NodeJS.Timeout; + return (...args: Parameters) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => func(...args), delay); + }; +} \ No newline at end of file diff --git a/crop-x/src/app/(app)/central-config/user/department/page.tsx b/crop-x/src/app/(app)/central-config/user/department/page.tsx index ce7b1b3..577d0a0 100644 --- a/crop-x/src/app/(app)/central-config/user/department/page.tsx +++ b/crop-x/src/app/(app)/central-config/user/department/page.tsx @@ -16,6 +16,12 @@ import { DepartmentTree } from './components/DepartmentTree'; import { DepartmentFormDialog } from './components/DepartmentFormDialog'; import { DepartmentDeleteDialog } from './components/DepartmentDeleteDialog'; import { DepartmentInstructions } from './components/DepartmentInstructions'; +import { + fetchDepartmentTree, + transformDepartmentList, + flattenDepartments, + type DepartmentTreeState +} from './components/departmentApi'; // 部门管理状态管理 interface DepartmentManagementState { @@ -116,151 +122,63 @@ export default function DepartmentManagementPage() { try { dispatch({ type: 'SET_LOADING', payload: true }); - // 暂时使用mock数据,后续可以替换为API调用 - const mockDepartments: Department[] = [ - { - id: 'dept-1', - name: '技术部', - code: 'TECH', - level: 1, - manager: '王技术', - phone: '13800138001', - email: 'tech@example.com', - description: '负责技术研发和系统维护', - sort: 1, - status: 'active', - createdAt: '2024-01-01T00:00:00', - updatedAt: '2024-01-01T00:00:00', - children: [ - { - id: 'dept-1-1', - parentId: 'dept-1', - name: '研发组', - code: 'TECH-RD', - level: 2, - manager: '李研发', - phone: '13800138011', - description: '负责系统研发', - sort: 1, - status: 'active', - createdAt: '2024-01-01T00:00:00', - updatedAt: '2024-01-01T00:00:00', - }, - { - id: 'dept-1-2', - parentId: 'dept-1', - name: '运维组', - code: 'TECH-OPS', - level: 2, - manager: '张运维', - phone: '13800138012', - description: '负责系统运维', - sort: 2, - status: 'active', - createdAt: '2024-01-01T00:00:00', - updatedAt: '2024-01-01T00:00:00', - }, - ], - }, - { - id: 'dept-2', - name: '管理部', - code: 'ADMIN', - level: 1, - manager: '赵管理', - phone: '13800138002', - email: 'admin@example.com', - description: '负责行政管理', - sort: 2, - status: 'active', - createdAt: '2024-01-01T00:00:00', - updatedAt: '2024-01-01T00:00:00', - children: [ - { - id: 'dept-2-1', - parentId: 'dept-2', - name: '人事组', - code: 'ADMIN-HR', - level: 2, - manager: '孙人事', - phone: '13800138021', - description: '负责人力资源管理', - sort: 1, - status: 'active', - createdAt: '2024-01-01T00:00:00', - updatedAt: '2024-01-01T00:00:00', - }, - { - id: 'dept-2-2', - parentId: 'dept-2', - name: '财务组', - code: 'ADMIN-FIN', - level: 2, - manager: '周财务', - phone: '13800138022', - description: '负责财务管理', - sort: 2, - status: 'active', - createdAt: '2024-01-01T00:00:00', - updatedAt: '2024-01-01T00:00:00', - }, - ], - }, - { - id: 'dept-3', - name: '作业部', - code: 'OPS', - level: 1, - manager: '吴作业', - phone: '13800138003', - email: 'ops@example.com', - description: '负责农机作业管理', - sort: 3, - status: 'active', - createdAt: '2024-01-01T00:00:00', - updatedAt: '2024-01-01T00:00:00', - children: [ - { - id: 'dept-3-1', - parentId: 'dept-3', - name: '第一作业组', - code: 'OPS-T1', - level: 2, - manager: '郑组长', - phone: '13800138031', - description: '负责区域A作业', - sort: 1, - status: 'active', - createdAt: '2024-01-01T00:00:00', - updatedAt: '2024-01-01T00:00:00', - }, - { - id: 'dept-3-2', - parentId: 'dept-3', - name: '第二作业组', - code: 'OPS-T2', - level: 2, - manager: '钱组长', - phone: '13800138032', - description: '负责区域B作业', - sort: 2, - status: 'active', - createdAt: '2024-01-01T00:00:00', - updatedAt: '2024-01-01T00:00:00', - }, - ], - }, - ]; + // 使用API调用获取部门树形数据 + const response = await fetchDepartmentTree({ + include_inactive: false, + include_members: true, + }); - dispatch({ type: 'SET_DEPARTMENTS', payload: mockDepartments }); + if (!response.success) { + throw new Error(response.message || '获取部门数据失败'); + } + + // 转换API数据为页面所需的格式 + const departments = transformDepartmentList(response.data); + + // 转换为与现有页面兼容的数据格式 + const compatibleDepartments: Department[] = departments.map(dept => ({ + id: dept.id, + name: dept.name, + code: dept.code, + level: dept.level + 1, // API的level从0开始,页面从1开始 + manager: dept.manager, // 从API的manager_name字段获取 + phone: dept.phone, // 从API的manager_phone字段获取 + email: dept.email, // 从API的manager_email字段获取 + description: dept.description, + sort: dept.sortOrder, + status: dept.status as 'active' | 'inactive', + parentId: dept.parentId || undefined, + createdAt: dept.createdAt, + updatedAt: dept.updatedAt, + children: dept.children.map(child => ({ + id: child.id, + name: child.name, + code: child.code, + level: child.level + 1, + manager: child.manager, // 从API的manager_name字段获取 + phone: child.phone, // 从API的manager_phone字段获取 + email: child.email, // 从API的manager_email字段获取 + description: child.description, + sort: child.sortOrder, + status: child.status as 'active' | 'inactive', + parentId: child.parentId || undefined, + createdAt: child.createdAt, + updatedAt: child.updatedAt, + })), + })); + + dispatch({ type: 'SET_DEPARTMENTS', payload: compatibleDepartments }); // 默认展开所有一级部门 - dispatch({ type: 'SET_EXPANDED_IDS', payload: new Set(mockDepartments.map(d => d.id)) }); + dispatch({ type: 'SET_EXPANDED_IDS', payload: new Set(compatibleDepartments.map(d => d.id)) }); + + toast.success(`成功加载 ${compatibleDepartments.length} 个部门`); } catch (error) { console.error('Failed to load departments:', error); dispatch({ type: 'SET_ERROR', payload: error instanceof Error ? error.message : '加载部门数据失败' }); + toast.error('加载部门数据失败'); } }; @@ -303,6 +221,11 @@ export default function DepartmentManagementPage() { dispatch({ type: 'SET_EXPANDED_IDS', payload: allIds }); }; + // 刷新数据 + const refreshData = () => { + loadDepartments(); + }; + // 收起全部 const collapseAll = () => { dispatch({ type: 'SET_EXPANDED_IDS', payload: new Set() }); @@ -533,7 +456,7 @@ export default function DepartmentManagementPage() { return (
{/* 页面标题 */} - handleAdd()} /> + handleAdd()} onRefresh={refreshData} loading={state.loading} /> {/* 统计卡片 */} @@ -565,6 +488,7 @@ export default function DepartmentManagementPage() { editingDepartment={state.editingDepartment} parentDepartment={state.parentDepartment} onSave={handleSave} + refreshDepartmentTree={refreshData} /> {/* 删除确认对话框 */} diff --git a/crop-x/src/app/(app)/central-config/user/role/components/RoleList.tsx b/crop-x/src/app/(app)/central-config/user/role/components/RoleList.tsx index b1c9513..7484fc7 100644 --- a/crop-x/src/app/(app)/central-config/user/role/components/RoleList.tsx +++ b/crop-x/src/app/(app)/central-config/user/role/components/RoleList.tsx @@ -42,7 +42,7 @@ export function RoleList({ return status === 'active' ? ( 启用 ) : ( - 禁用 + 停用 ); };