/** * 用途:新建用户表单弹窗(用户名/显示名称/密码/邮箱/角色) * 必需 props:onClose(关闭)、onCreated(创建成功后回调,页面负责关窗并刷新列表) * 已知限制:成功 toast 沿用原有反馈点,由本组件内部触发 */ import { useState } from 'react'; import { userApi } from '@/api/user'; import { toast } from '@/hooks/useToast'; import { ModalOverlay } from '@/components/ModalOverlay'; import { validateUserForm } from '../utils'; import type { FormErrors } from '../types'; interface CreateUserModalProps { onClose: () => void; onCreated: () => void; } export function CreateUserModal({ onClose, onCreated }: CreateUserModalProps) { const [username, setUsername] = useState(''); const [displayName, setDisplayName] = useState(''); const [password, setPassword] = useState(''); const [email, setEmail] = useState(''); const [role, setRole] = useState('developer'); const [submitting, setSubmitting] = useState(false); const [errors, setErrors] = useState({}); const handleSubmit = async () => { const validation = validateUserForm({ username, password, email }); setErrors(validation); if (Object.keys(validation).length > 0) return; setSubmitting(true); try { await userApi.create({ username, display_name: displayName, password, email: email || undefined, role }); toast('success', '用户创建成功'); onCreated(); } catch (e: any) { toast('error', e?.response?.data?.detail || '创建失败'); } finally { setSubmitting(false); } }; return (

新建用户

{ setUsername(e.target.value); if (errors.username) setErrors((p) => ({ ...p, username: undefined })); }} className={`w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border rounded-lg text-sm focus:border-blue-400 focus:outline-none ${errors.username ? 'border-red-400' : 'border-[var(--dg-border)]'}`} placeholder="3-30位字母、数字或下划线" /> {errors.username &&

{errors.username}

}
setDisplayName(e.target.value)} className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none" placeholder="用户的显示名称" />
{ setPassword(e.target.value); if (errors.password) setErrors((p) => ({ ...p, password: undefined })); }} className={`w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border rounded-lg text-sm focus:border-blue-400 focus:outline-none ${errors.password ? 'border-red-400' : 'border-[var(--dg-border)]'}`} placeholder="至少6位" /> {errors.password &&

{errors.password}

}
{ setEmail(e.target.value); if (errors.email) setErrors((p) => ({ ...p, email: undefined })); }} className={`w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border rounded-lg text-sm focus:border-blue-400 focus:outline-none ${errors.email ? 'border-red-400' : 'border-[var(--dg-border)]'}`} placeholder="example@company.com" /> {errors.email &&

{errors.email}

}
); }