Files
aiops/frontend/src/components/system/LdapImportDialog.tsx
T
attacker 5364d93ed7 fix(q1): 修复 tsc -b 构建中断 — 回退不安全的 unknown→any + 移除 Ansible 残留引用
- 781处 tsc -b 错误清零(unknown 属性访问/隐式any返回类型/类型转换不匹配)
- 移除 App.tsx 中已删除的 AnsibleManager/AnsibleWorkflow 导入和路由
- 修复 DeploymentView 中 _prefix 解构导致的属性不存在错误
- 修复 Header/Sidebar/PipelineEditor 中无效的类型断言
- asList() 返回类型 unknown[]→any[], 恢复 AgentMonitor 正常工作
- 放宽 noImplicitAny(临时), 待 types/ 定义完善后逐步恢复
- ESLint: unused=0, console=0, any=1041(Q1 后续降低)
- npm run build: ✅ 0 errors
2026-06-25 07:17:47 +08:00

183 lines
7.6 KiB
TypeScript

// 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>
);
}