refactor: 拆分SystemUserTab.tsx(588→311行)提取UserDetailDialog+CsvImportDialog+LdapImportDialog子组件
This commit is contained in:
1 parent
367232f2b3
commit
2b5cbfb992
4 files changed
+470
-315
No files matched your search
@@ -0,0 +1,182 @@
|
||||
// LDAP 用户导入弹窗(OU 选择 + 用户列表多选 + 导入结果) // Logan.Li
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Network, Loader2, RefreshCw, CheckCircle, XCircle } from 'lucide-react';
|
||||
|
||||
interface LdapImportDialogProps {
|
||||
importDialogOpen: 'csv' | 'ldap' | null;
|
||||
setImportDialogOpen: (v: 'csv' | 'ldap' | null) => void;
|
||||
ldapUsers: any[];
|
||||
setLdapUsers: (u: any[]) => void;
|
||||
ldapSelected: Set<string>;
|
||||
setLdapSelected: (s: Set<string>) => void;
|
||||
ldapLoading: boolean;
|
||||
ldapImportOUs: any[];
|
||||
ldapImportOUDN: string;
|
||||
setLdapImportOUDN: (v: string) => void;
|
||||
loadLdapUsers: (ouDn?: string) => void;
|
||||
handleLdapImport: () => void;
|
||||
importing: boolean;
|
||||
importResult: any;
|
||||
setImportResult: (r: any) => void;
|
||||
}
|
||||
|
||||
// LdapImportDialog 展示 LDAP 用户检索和多选导入流程 // Logan.Li
|
||||
export function LdapImportDialog({
|
||||
importDialogOpen,
|
||||
setImportDialogOpen,
|
||||
ldapUsers,
|
||||
setLdapUsers,
|
||||
ldapSelected,
|
||||
setLdapSelected,
|
||||
ldapLoading,
|
||||
ldapImportOUs,
|
||||
ldapImportOUDN,
|
||||
setLdapImportOUDN,
|
||||
loadLdapUsers,
|
||||
handleLdapImport,
|
||||
importing,
|
||||
importResult,
|
||||
setImportResult,
|
||||
}: LdapImportDialogProps) {
|
||||
return (
|
||||
<Dialog open={importDialogOpen === 'ldap'} onOpenChange={(v) => !v && setImportDialogOpen(null)}>
|
||||
<DialogContent className="bg-slate-900 border-slate-800 max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-slate-100 flex items-center gap-2">
|
||||
<Network className="h-5 w-5 text-cyan-400" />
|
||||
从 LDAP 导入用户
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-slate-500">
|
||||
从 LDAP 服务器检索用户列表,选择需要导入的用户
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{!importResult ? (
|
||||
<>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-slate-500 whitespace-nowrap">选择 OU</span>
|
||||
<select
|
||||
className="bg-slate-800 border border-slate-700 text-slate-200 text-xs rounded-md px-2 py-1.5 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||
value={ldapImportOUDN}
|
||||
onChange={(e) => {
|
||||
setLdapImportOUDN(e.target.value);
|
||||
loadLdapUsers(e.target.value);
|
||||
}}
|
||||
>
|
||||
<option value="">根 (全部)</option>
|
||||
{ldapImportOUs.map((ou) => (
|
||||
<option key={ou.dn} value={ou.dn}>{ou.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<Button variant="outline" size="sm" className="border-slate-700 text-slate-300 ml-auto" onClick={() => loadLdapUsers(ldapImportOUDN)} disabled={ldapLoading}>
|
||||
{ldapLoading ? <Loader2 className="h-4 w-4 mr-1.5 animate-spin" /> : <RefreshCw className="h-4 w-4 mr-1.5" />}
|
||||
刷新
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-slate-500">
|
||||
{ldapUsers.length > 0 ? `找到 ${ldapUsers.length} 个用户` : '选择 OU 或点击刷新获取用户列表'}
|
||||
</p>
|
||||
|
||||
{ldapUsers.length > 0 && (
|
||||
<div className="max-h-80 overflow-auto rounded-lg border border-slate-800">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="border-slate-800 sticky top-0 bg-slate-900">
|
||||
<TableHead className="w-10 text-slate-400 text-xs">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="accent-blue-600"
|
||||
checked={ldapSelected.size === ldapUsers.length && ldapUsers.length > 0}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setLdapSelected(new Set(ldapUsers.map((u) => u.dn)));
|
||||
} else {
|
||||
setLdapSelected(new Set());
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TableHead>
|
||||
<TableHead className="text-slate-400 text-xs">用户名</TableHead>
|
||||
<TableHead className="text-slate-400 text-xs">显示名</TableHead>
|
||||
<TableHead className="text-slate-400 text-xs">邮箱</TableHead>
|
||||
<TableHead className="text-slate-400 text-xs">DN</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{ldapUsers.map((u) => (
|
||||
<TableRow key={u.dn} className="border-slate-800 hover:bg-slate-800/50">
|
||||
<TableCell>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="accent-blue-600"
|
||||
checked={ldapSelected.has(u.dn)}
|
||||
onChange={(e) => {
|
||||
const next = new Set(ldapSelected);
|
||||
if (e.target.checked) next.add(u.dn);
|
||||
else next.delete(u.dn);
|
||||
setLdapSelected(next);
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="text-slate-200 text-xs">{u.uid}</TableCell>
|
||||
<TableCell className="text-slate-300 text-xs">{u.cn}</TableCell>
|
||||
<TableCell className="text-slate-400 text-xs">{u.mail}</TableCell>
|
||||
<TableCell className="text-slate-500 text-xs font-mono truncate max-w-[200px]">{u.dn}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" className="border-slate-700 text-slate-300" onClick={() => { setImportDialogOpen(null); setLdapUsers([]); }}>
|
||||
取消
|
||||
</Button>
|
||||
<Button className="bg-blue-600 hover:bg-blue-700 text-white" onClick={handleLdapImport} disabled={importing || ldapSelected.size === 0}>
|
||||
{importing ? '导入中...' : `导入选中 (${ldapSelected.size})`}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</>
|
||||
) : (
|
||||
<div className="space-y-4 py-2">
|
||||
<div className={`flex items-center gap-3 p-4 rounded-lg ${importResult.code === 200 ? 'bg-emerald-500/10' : 'bg-red-500/10'}`}>
|
||||
{importResult.code === 200 ? (
|
||||
<CheckCircle className="h-6 w-6 text-emerald-400 shrink-0" />
|
||||
) : (
|
||||
<XCircle className="h-6 w-6 text-red-400 shrink-0" />
|
||||
)}
|
||||
<span className="text-sm text-slate-200">{importResult.message}</span>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button className="bg-blue-600 hover:bg-blue-700 text-white" onClick={() => { setImportDialogOpen(null); setLdapUsers([]); setImportResult(null); }}>
|
||||
完成
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user