From 80171778b5246e1930d760430f32b560d031c765 Mon Sep 17 00:00:00 2001 From: peng Date: Tue, 11 Nov 2025 21:10:14 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=9F=E4=BA=A7=E7=AE=A1=E7=90=86=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=20-=20=E6=8F=90=E4=BA=A4=E7=94=A8=E6=88=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E9=A1=B5=E9=9D=A2=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/AddUserModal.tsx | 353 +++++++++++++++++ .../components/EditUserModal.tsx | 359 ++++++++++++++++++ .../components/PasswordInput.tsx | 85 +++++ .../components/enterpriseApi.ts | 121 ++++++ .../user-management/constants/userTypes.ts | 38 ++ .../tenant/user-management/page.tsx | 42 +- 6 files changed, 996 insertions(+), 2 deletions(-) create mode 100644 src/app/(app)/central-config/tenant/user-management/components/AddUserModal.tsx create mode 100644 src/app/(app)/central-config/tenant/user-management/components/EditUserModal.tsx create mode 100644 src/app/(app)/central-config/tenant/user-management/components/PasswordInput.tsx create mode 100644 src/app/(app)/central-config/tenant/user-management/components/enterpriseApi.ts create mode 100644 src/app/(app)/central-config/tenant/user-management/constants/userTypes.ts diff --git a/src/app/(app)/central-config/tenant/user-management/components/AddUserModal.tsx b/src/app/(app)/central-config/tenant/user-management/components/AddUserModal.tsx new file mode 100644 index 0000000..32a52f9 --- /dev/null +++ b/src/app/(app)/central-config/tenant/user-management/components/AddUserModal.tsx @@ -0,0 +1,353 @@ +/** + * filekorolheader: 新增用户弹窗组件 - 新建用户功能界面 + * 功能:用户信息录入表单、表单验证、数据提交、状态管理 + * 路径:/central-config/tenant/user-management/components/AddUserModal + * 规范:遵循crop-x-new/docs/开发项目规范.md,使用shadcn语义化样式,支持暗色主题 + */ +'use client'; + +import { useState, useEffect } from 'react'; +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 { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { toast } from 'sonner'; +import { fetchEnterprisesForDropdown, transformEnterprisesToOptions, type EnterpriseOption } from './enterpriseApi'; +import { PasswordInput } from './PasswordInput'; +import { USER_TYPE_OPTIONS, USER_TYPES } from '../constants/userTypes'; + +interface AddUserModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onSuccess?: () => void; +} + +interface AddUserFormData { + username: string; + password: string; + fullName: string; + phone: string; + email: string; + userType: typeof USER_TYPES[keyof typeof USER_TYPES]; + enterpriseId?: string; +} + +export function AddUserModal({ open, onOpenChange, onSuccess }: AddUserModalProps) { + const [formData, setFormData] = useState({ + username: '', + password: '', + fullName: '', + phone: '', + email: '', + userType: 'tenant', + enterpriseId: '', + }); + + const [errors, setErrors] = useState>({}); + const [isSubmitting, setIsSubmitting] = useState(false); + const [enterprises, setEnterprises] = useState([]); + const [enterpriseOptions, setEnterpriseOptions] = useState>([]); + const [isLoadingEnterprises, setIsLoadingEnterprises] = useState(false); + + // 加载企业列表数据 + const loadEnterprises = async () => { + try { + setIsLoadingEnterprises(true); + console.log('🏢 AddUserModal - 开始加载企业列表'); + + const enterpriseData = await fetchEnterprisesForDropdown(); + setEnterprises(enterpriseData); + + const options = transformEnterprisesToOptions(enterpriseData); + setEnterpriseOptions(options); + + console.log('🏢 AddUserModal - 企业列表加载完成:', { + total: enterpriseData.length, + options: options.length, + }); + } catch (error) { + console.error('🏢 AddUserModal - 加载企业列表失败:', error); + toast.error('加载企业列表失败,请刷新页面重试'); + } finally { + setIsLoadingEnterprises(false); + } + }; + + // 当弹窗打开时加载企业列表 + useEffect(() => { + if (open) { + loadEnterprises(); + } + }, [open]); + + const validateForm = () => { + const newErrors: Record = {}; + + if (!formData.username.trim()) { + newErrors.username = '用户名不能为空'; + } + + if (!formData.password.trim()) { + newErrors.password = '密码不能为空'; + } else if (formData.password.length < 6) { + newErrors.password = '密码长度至少6位'; + } + + if (!formData.fullName.trim()) { + newErrors.fullName = '姓名不能为空'; + } + + if (!formData.phone.trim()) { + newErrors.phone = '电话不能为空'; + } else if (!/^1[3-9]\d{9}$/.test(formData.phone)) { + newErrors.phone = '请输入正确的手机号码'; + } + + if (formData.email.trim() && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { + newErrors.email = '邮箱格式不正确'; + } + + if (formData.userType === 'tenant' && !formData.enterpriseId?.trim()) { + newErrors.enterpriseId = '企业管理员必须选择所属企业'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async () => { + if (!validateForm()) { + return; + } + + setIsSubmitting(true); + + try { + // TODO: 调用API创建用户 + // 暂时模拟API调用 + await new Promise(resolve => setTimeout(resolve, 1000)); + + const submitData = { + username: formData.username, + password: formData.password, + full_name: formData.fullName, + phone: formData.phone, + email: formData.email || undefined, + user_type: formData.userType, + ...(formData.userType === 'tenant' && formData.enterpriseId ? { enterprise_id: formData.enterpriseId } : {}), + }; + + console.log('新增用户数据:', submitData); + + toast.success('用户创建成功'); + onOpenChange(false); + onSuccess?.(); + + // 重置表单 + setFormData({ + username: '', + password: '', + fullName: '', + phone: '', + email: '', + userType: 'tenant', + enterpriseId: '', + }); + setErrors({}); + } catch (error) { + console.error('创建用户失败:', error); + toast.error('创建用户失败,请重试'); + } finally { + setIsSubmitting(false); + } + }; + + const handleInputChange = (field: keyof AddUserFormData, value: string) => { + setFormData(prev => ({ ...prev, [field]: value })); + // 清除对应字段的错误 + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: '' })); + } + }; + + return ( + + + + 新增用户 + + 创建新的系统用户账户 + + + +
+ {/* 第一行:用户名、密码 */} +
+
+ + handleInputChange('username', e.target.value)} + placeholder="请输入用户名" + className={errors.username ? 'border-red-500' : ''} + /> + {errors.username && ( +

{errors.username}

+ )} +
+ + handleInputChange('password', value)} + placeholder="请输入密码" + required={true} + error={errors.password} + /> +
+ + {/* 第二行:姓名、电话 */} +
+
+ + handleInputChange('fullName', e.target.value)} + placeholder="请输入姓名" + className={errors.fullName ? 'border-red-500' : ''} + /> + {errors.fullName && ( +

{errors.fullName}

+ )} +
+ +
+ + handleInputChange('phone', e.target.value)} + placeholder="请输入手机号码" + className={errors.phone ? 'border-red-500' : ''} + /> + {errors.phone && ( +

{errors.phone}

+ )} +
+
+ + {/* 第三行:邮箱 */} +
+
+ + handleInputChange('email', e.target.value)} + placeholder="请输入邮箱(可选)" + className={errors.email ? 'border-red-500' : ''} + /> + {errors.email && ( +

{errors.email}

+ )} +
+
+ + {/* 第四行:用户类型、所属企业 */} +
+
+ + +
+ + {formData.userType === 'tenant' ? ( +
+ + + {errors.enterpriseId && ( +

{errors.enterpriseId}

+ )} +
+ ) : ( +
+ + +
+ )} +
+
+ + + + + +
+
+ ); +} \ No newline at end of file diff --git a/src/app/(app)/central-config/tenant/user-management/components/EditUserModal.tsx b/src/app/(app)/central-config/tenant/user-management/components/EditUserModal.tsx new file mode 100644 index 0000000..64e627b --- /dev/null +++ b/src/app/(app)/central-config/tenant/user-management/components/EditUserModal.tsx @@ -0,0 +1,359 @@ +/** + * filekorolheader: 修改用户弹窗组件 - 编辑用户功能界面 + * 功能:用户信息编辑表单、表单验证、数据更新、状态管理 + * 路径:/central-config/tenant/user-management/components/EditUserModal + * 规范:遵循crop-x-new/docs/开发项目规范.md,使用shadcn语义化样式,支持暗色主题 + */ +'use client'; + +import { useState, useEffect } from 'react'; +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 { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { toast } from 'sonner'; +import { User } from '../types'; +import { fetchEnterprisesForDropdown, transformEnterprisesToOptions, type EnterpriseOption } from './enterpriseApi'; +import { PasswordInput } from './PasswordInput'; +import { USER_TYPE_OPTIONS, USER_TYPES } from '../constants/userTypes'; + +interface EditUserModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; + user: User | null; + onSuccess?: () => void; +} + +interface EditUserFormData { + username: string; + password: string; + fullName: string; + phone: string; + email: string; + userType: typeof USER_TYPES[keyof typeof USER_TYPES]; + enterpriseId?: string; +} + +export function EditUserModal({ open, onOpenChange, user, onSuccess }: EditUserModalProps) { + const [formData, setFormData] = useState({ + username: '', + password: '', + fullName: '', + phone: '', + email: '', + userType: 'tenant', + enterpriseId: '', + }); + + const [errors, setErrors] = useState>({}); + const [isSubmitting, setIsSubmitting] = useState(false); + const [enterprises, setEnterprises] = useState([]); + const [enterpriseOptions, setEnterpriseOptions] = useState>([]); + const [isLoadingEnterprises, setIsLoadingEnterprises] = useState(false); + + // 加载企业列表数据 + const loadEnterprises = async () => { + try { + setIsLoadingEnterprises(true); + console.log('🏢 EditUserModal - 开始加载企业列表'); + + const enterpriseData = await fetchEnterprisesForDropdown(); + setEnterprises(enterpriseData); + + const options = transformEnterprisesToOptions(enterpriseData); + setEnterpriseOptions(options); + + console.log('🏢 EditUserModal - 企业列表加载完成:', { + total: enterpriseData.length, + options: options.length, + }); + } catch (error) { + console.error('🏢 EditUserModal - 加载企业列表失败:', error); + toast.error('加载企业列表失败,请刷新页面重试'); + } finally { + setIsLoadingEnterprises(false); + } + }; + + // 当弹窗打开时加载企业列表 + useEffect(() => { + if (open) { + loadEnterprises(); + } + }, [open]); + + // 编辑弹窗不预填充用户数据,保持空表单 + + const validateForm = () => { + const newErrors: Record = {}; + + if (!formData.username.trim()) { + newErrors.username = '用户名不能为空'; + } + + if (!formData.password.trim()) { + newErrors.password = '密码不能为空'; + } else if (formData.password.length < 6) { + newErrors.password = '密码长度至少6位'; + } + + if (!formData.fullName.trim()) { + newErrors.fullName = '姓名不能为空'; + } + + if (!formData.phone.trim()) { + newErrors.phone = '电话不能为空'; + } else if (!/^1[3-9]\d{9}$/.test(formData.phone)) { + newErrors.phone = '请输入正确的手机号码'; + } + + if (formData.email.trim() && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { + newErrors.email = '邮箱格式不正确'; + } + + if (formData.userType === 'tenant' && !formData.enterpriseId?.trim()) { + newErrors.enterpriseId = '企业管理员必须选择所属企业'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async () => { + if (!validateForm() || !user) { + return; + } + + setIsSubmitting(true); + + try { + // TODO: 调用API更新用户 + // 暂时模拟API调用 + await new Promise(resolve => setTimeout(resolve, 1000)); + + const submitData = { + id: user.id, + username: formData.username, + password: formData.password, + full_name: formData.fullName, + phone: formData.phone, + email: formData.email || undefined, + user_type: formData.userType, + ...(formData.userType === 'tenant' && formData.enterpriseId ? { enterprise_id: formData.enterpriseId } : {}), + }; + + console.log('更新用户数据:', submitData); + + toast.success('用户信息更新成功'); + onOpenChange(false); + onSuccess?.(); + } catch (error) { + console.error('更新用户失败:', error); + toast.error('更新用户失败,请重试'); + } finally { + setIsSubmitting(false); + } + }; + + const handleInputChange = (field: keyof EditUserFormData, value: string) => { + setFormData(prev => ({ ...prev, [field]: value })); + // 清除对应字段的错误 + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: '' })); + } + }; + + return ( + + + + 编辑用户 + + 修改用户基本信息 + + + + {!user ? ( +
+ 请选择要编辑的用户 +
+ ) : ( + <> +
+ {/* 第一行:用户名、密码 */} +
+
+ + handleInputChange('username', e.target.value)} + placeholder="请输入用户名" + className={errors.username ? 'border-red-500' : ''} + /> + {errors.username && ( +

{errors.username}

+ )} +
+ + handleInputChange('password', value)} + placeholder="请输入密码" + required={true} + error={errors.password} + /> +
+ + {/* 第二行:姓名、电话 */} +
+
+ + handleInputChange('fullName', e.target.value)} + placeholder="请输入姓名" + className={errors.fullName ? 'border-red-500' : ''} + /> + {errors.fullName && ( +

{errors.fullName}

+ )} +
+ +
+ + handleInputChange('phone', e.target.value)} + placeholder="请输入手机号码" + className={errors.phone ? 'border-red-500' : ''} + /> + {errors.phone && ( +

{errors.phone}

+ )} +
+
+ + {/* 第三行:邮箱 */} +
+
+ + handleInputChange('email', e.target.value)} + placeholder="请输入邮箱(可选)" + className={errors.email ? 'border-red-500' : ''} + /> + {errors.email && ( +

{errors.email}

+ )} +
+
+ + {/* 第四行:用户类型、所属企业 */} +
+
+ + +
+ + {formData.userType === 'tenant' ? ( +
+ + + {errors.enterpriseId && ( +

{errors.enterpriseId}

+ )} +
+ ) : ( +
+ + +
+ )} +
+ + {/* 用户ID显示 */} +
+ 用户ID: {user.id} +
+
+ + + + + + + )} +
+
+ ); +} \ No newline at end of file diff --git a/src/app/(app)/central-config/tenant/user-management/components/PasswordInput.tsx b/src/app/(app)/central-config/tenant/user-management/components/PasswordInput.tsx new file mode 100644 index 0000000..ec03634 --- /dev/null +++ b/src/app/(app)/central-config/tenant/user-management/components/PasswordInput.tsx @@ -0,0 +1,85 @@ +/** + * filekorolheader: 密码输入组件 - 支持显示/隐藏密码功能 + * 功能:密码输入、显示/隐藏切换、表单验证 + * 路径:/central-config/tenant/user-management/components/PasswordInput + * 规范:遵循crop-x-new/docs/开发项目规范.md,使用shadcn语义化样式,支持暗色主题 + */ +'use client'; + +import { useState } from 'react'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Eye, EyeOff } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface PasswordInputProps { + id: string; + label: string; + value: string; + onChange: (value: string) => void; + placeholder?: string; + required?: boolean; + error?: string; + disabled?: boolean; +} + +export function PasswordInput({ + id, + label, + value, + onChange, + placeholder = "请输入密码", + required = false, + error, + disabled = false, +}: PasswordInputProps) { + const [showPassword, setShowPassword] = useState(false); + + const togglePasswordVisibility = () => { + setShowPassword(!showPassword); + }; + + return ( +
+ +
+ onChange(e.target.value)} + placeholder={placeholder} + disabled={disabled} + className={cn( + error && 'border-red-500', + 'pr-10' // 为眼睛图标预留空间 + )} + /> + +
+ {error && ( +

{error}

+ )} +
+ ); +} \ No newline at end of file diff --git a/src/app/(app)/central-config/tenant/user-management/components/enterpriseApi.ts b/src/app/(app)/central-config/tenant/user-management/components/enterpriseApi.ts new file mode 100644 index 0000000..aa9b735 --- /dev/null +++ b/src/app/(app)/central-config/tenant/user-management/components/enterpriseApi.ts @@ -0,0 +1,121 @@ +/** + * filekorolheader: 企业下拉列表API接口 - 用户管理页面企业数据获取服务 + * 功能:企业列表查询、下拉框数据准备、错误处理 + * 路径:/central-config/tenant/user-management/components/enterpriseApi + * 规范:遵循crop-x/docs/开发项目规范.md,使用SDK API调用,TypeScript类型安全 + */ + +import { getAuthToken } from "@/utils/token.ts"; +import { + listTenantsApiV1TenantsGet +} from "@/lib/api/sdk.gen"; + +// 企业数据类型(简化版,用于下拉框) +export interface EnterpriseOption { + id: string; + company_name: string; + tenant_code: string; + is_active: boolean; +} + +// API响应数据类型 +export interface EnterpriseApiResponse { + data: EnterpriseOption[]; + total: number; + page: number; + size: number; + total_pages: number; + has_next: boolean; + has_prev: boolean; +} + +// 查询参数接口 +export interface EnterpriseQueryParams { + search?: string; + audit_status?: string; + page?: number; + size?: number; + order_by?: string; + sort_order?: 'asc' | 'desc'; +} + +/** + * 获取企业列表数据(用于下拉框) + * 固定查询100条数据,获取所有活跃企业 + */ +export async function fetchEnterprisesForDropdown(params: EnterpriseQueryParams = {}): Promise { + try { + console.log('🏢 用户管理页面 - 获取企业下拉列表数据'); + + // 构建查询参数,固定查询100条数据 + const queryParams: any = { + page: 1, + size: 100, // 固定查询100条 + sort_order: 'desc', + order_by: 'created_at', + // 只查询活跃的企业 + is_active: true, + }; + + // 添加可选参数 + if (params.search) queryParams.search = params.search; + if (params.audit_status) queryParams.audit_status = params.audit_status; + + // 获取认证token + const token = getAuthToken(); + + // 使用SDK API调用企业查询接口 + const response = await listTenantsApiV1TenantsGet({ + 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响应:', { + total: data?.total || 0, + dataCount: data?.data?.length || 0, + }); + + // 转换响应数据格式,只返回需要的字段 + const enterprises: EnterpriseOption[] = (data?.data || []).map((tenant: any) => ({ + id: tenant.id, + company_name: tenant.company_name, + tenant_code: tenant.tenant_code, + is_active: tenant.is_active, + })); + + console.log('🏢 转换后的企业下拉列表数据:', enterprises.length, '条'); + + return enterprises; + } catch (error) { + console.error('🏢 获取企业下拉列表失败:', error); + throw error; + } +} + +/** + * 将企业数据转换为下拉框选项格式 + */ +export function transformEnterprisesToOptions(enterprises: EnterpriseOption[]): Array<{ + value: string; + label: string; +}> { + return enterprises + .filter(enterprise => enterprise.is_active) // 只显示活跃企业 + .map(enterprise => ({ + value: enterprise.id, + label: `${enterprise.company_name} (${enterprise.tenant_code})`, + })) + .sort((a, b) => a.label.localeCompare(b.label, 'zh-CN')); // 按中文名称排序 +} \ No newline at end of file diff --git a/src/app/(app)/central-config/tenant/user-management/constants/userTypes.ts b/src/app/(app)/central-config/tenant/user-management/constants/userTypes.ts new file mode 100644 index 0000000..7c10fb6 --- /dev/null +++ b/src/app/(app)/central-config/tenant/user-management/constants/userTypes.ts @@ -0,0 +1,38 @@ +/** + * filekorolheader: 用户类型常量定义 - 用户类型映射关系 + * 功能:用户类型枚举、中文名称映射、下拉选项数据 + * 路径:/central-config/tenant/user-management/constants/userTypes + * 规范:遵循crop-x-new/docs/开发项目规范.md,使用TypeScript类型安全 + */ + +// 用户类型枚举 +export const USER_TYPES = { + TENANT: 'tenant', + SYSTEM: 'system', +} as const; + +// 用户类型中文映射 +export const USER_TYPE_LABELS = { + [USER_TYPES.TENANT]: '企业管理员', + [USER_TYPES.SYSTEM]: '系统管理员', +} as const; + +// 用户类型下拉选项 +export const USER_TYPE_OPTIONS = [ + { + value: USER_TYPES.TENANT, + label: USER_TYPE_LABELS[USER_TYPES.TENANT], + }, + { + value: USER_TYPES.SYSTEM, + label: USER_TYPE_LABELS[USER_TYPES.SYSTEM], + }, +] as const; + +// 根据值获取标签 +export function getUserTypeLabel(type: string): string { + return USER_TYPE_LABELS[type as keyof typeof USER_TYPE_LABELS] || type; +} + +// 类型定义 +export type UserType = keyof typeof USER_TYPE_LABELS; \ No newline at end of file diff --git a/src/app/(app)/central-config/tenant/user-management/page.tsx b/src/app/(app)/central-config/tenant/user-management/page.tsx index a612d6d..3e0dd1c 100644 --- a/src/app/(app)/central-config/tenant/user-management/page.tsx +++ b/src/app/(app)/central-config/tenant/user-management/page.tsx @@ -11,6 +11,8 @@ import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Eye, Edit, Lock, UserX, UserCheck } from 'lucide-react'; import { UserDetailDialog } from './components/UserDetailDialog'; +import { AddUserModal } from './components/AddUserModal'; +import { EditUserModal } from './components/EditUserModal'; import { SearchFormPagination, SearchFieldConfig, TableColumnConfig } from '@/components/common/searchFormPagination'; import { fetchUsers, transformUserData, UsersQueryParams, User, UsersApiResponse, PaginationState } from './components/userManagementApi'; @@ -31,6 +33,8 @@ interface UserManagementState { sortOrder: 'asc' | 'desc'; selectedUser: User | null; showDetailDialog: boolean; + showAddDialog: boolean; + showEditDialog: boolean; } type UserManagementAction = @@ -42,6 +46,8 @@ type UserManagementAction = | { type: 'SET_PAGINATION'; payload: Partial } | { type: 'SET_SELECTED_USER'; payload: User | null } | { type: 'TOGGLE_DETAIL_DIALOG'; payload: boolean } + | { type: 'TOGGLE_ADD_DIALOG'; payload: boolean } + | { type: 'TOGGLE_EDIT_DIALOG'; payload: boolean } | { type: 'REFRESH_DATA' }; const userManagementReducer = (state: UserManagementState, action: UserManagementAction): UserManagementState => { @@ -68,6 +74,10 @@ const userManagementReducer = (state: UserManagementState, action: UserManagemen return { ...state, selectedUser: action.payload }; case 'TOGGLE_DETAIL_DIALOG': return { ...state, showDetailDialog: !state.showDetailDialog }; + case 'TOGGLE_ADD_DIALOG': + return { ...state, showAddDialog: !state.showAddDialog }; + case 'TOGGLE_EDIT_DIALOG': + return { ...state, showEditDialog: !state.showEditDialog }; case 'REFRESH_DATA': return { ...state, error: null }; default: @@ -96,6 +106,8 @@ const initialState: UserManagementState = { sortOrder: 'desc', selectedUser: null, showDetailDialog: false, + showAddDialog: false, + showEditDialog: false, }; export default function TenantUserManagementPage() { @@ -461,9 +473,20 @@ export default function TenantUserManagementPage() { // 编辑用户 const handleEdit = (user: User) => { - toast.info('编辑功能开发中...'); + dispatch({ type: 'SET_SELECTED_USER', payload: user }); + dispatch({ type: 'TOGGLE_EDIT_DIALOG', payload: true }); }; + // 新增用户 + const handleAdd = () => { + dispatch({ type: 'TOGGLE_ADD_DIALOG', payload: true }); + }; + + // 刷新数据(用于新增/编辑成功后重新加载数据) + const refreshData = useCallback(() => { + loadUsers({}); + }, [loadUsers]); + // 切换用户状态 const handleToggleStatus = (user: User) => { const newStatus = !user.isActive; @@ -525,7 +548,7 @@ export default function TenantUserManagementPage() { toast.info('新建用户功能开发中...')}> + } @@ -551,6 +574,21 @@ export default function TenantUserManagementPage() { onOpenChange={(open) => dispatch({ type: 'TOGGLE_DETAIL_DIALOG', payload: open })} user={state.selectedUser} /> + + {/* 新增用户对话框 */} + dispatch({ type: 'TOGGLE_ADD_DIALOG', payload: open })} + onSuccess={refreshData} + /> + + {/* 编辑用户对话框 */} + dispatch({ type: 'TOGGLE_EDIT_DIALOG', payload: open })} + user={state.selectedUser} + onSuccess={refreshData} + /> ); } \ No newline at end of file