import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { Link } from 'react-router' import { Users, RefreshCw } from 'lucide-react' import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { ScrollArea } from '@/components/ui/scroll-area' import { ldapApi, type LdapUserPreview, type LdapImportResult } from '@/api/ldap' import { toast } from 'sonner' import type { User } from '@/types' interface LdapImportDialogProps { open: boolean onOpenChange: (open: boolean) => void /** 本地用户列表(用于标记已导入,禁选) */ localUsers: User[] onImported: () => void } /** * 从 LDAP 导入用户弹窗 — 打开即拉取 LDAP 用户列表; * 已导入(LDAP 来源)置灰禁选;本地同名用户标注「将转为 LDAP」。 */ export const LdapImportDialog = memo(function LdapImportDialog({ open, onOpenChange, localUsers, onImported, }: LdapImportDialogProps) { const [loading, setLoading] = useState(false) const [importing, setImporting] = useState(false) const [users, setUsers] = useState([]) const [selected, setSelected] = useState>(new Set()) const [result, setResult] = useState(null) const [error, setError] = useState('') const localMap = useMemo(() => { const m = new Map() localUsers.forEach((u) => m.set(u.username, u)) return m }, [localUsers]) const loadUsers = useCallback(async () => { setLoading(true) setError('') setResult(null) try { const list = await ldapApi.listUsers() setUsers(list) setSelected(new Set()) } catch (err) { setError(err instanceof Error ? err.message : '加载 LDAP 用户失败') } finally { setLoading(false) } }, []) useEffect(() => { if (open) void loadUsers() }, [open, loadUsers]) const toggle = (username: string, disabled: boolean) => { if (disabled) return setSelected((prev) => { const next = new Set(prev) if (next.has(username)) next.delete(username) else next.add(username) return next }) } const handleImport = async () => { const picked = users.filter((u) => selected.has(u.username)) if (picked.length === 0) return setImporting(true) try { const r = await ldapApi.batchImport( picked.map((u) => ({ dn: u.dn, username: u.username, nickname: u.displayName, email: u.email, })) ) setResult(r) onImported() } catch (err) { toast.error(err instanceof Error ? err.message : '导入失败') } finally { setImporting(false) } } return ( 从 LDAP 导入用户 来自 LDAP 目录的用户;导入后使用 LDAP 密码登录,角色为普通用户 {result ? (
{result.successCount > 0 && ( 新增 {result.successCount} )} {result.updatedCount > 0 && 转为 LDAP {result.updatedCount}} {result.skippedCount > 0 && 跳过 {result.skippedCount}} {result.failedCount > 0 && 失败 {result.failedCount}}
{result.failedUsers.length > 0 && (
{result.failedUsers.map((f) => (

{f.username}:{f.error}

))}
)}
) : ( <>

{loading ? '加载 LDAP 用户…' : `共 ${users.length} 个用户,已选 ${selected.size} 个`}

{error ? (

{error}

请到 系统设置 检查 LDAP 配置与启用状态

) : (
{users.map((u) => { const local = localMap.get(u.username) const isLdapAlready = local?.['authSource'] === 'ldap' const willConvert = !!local && !isLdapAlready const disabled = isLdapAlready const checked = selected.has(u.username) return ( ) })} {!loading && users.length === 0 && (
目录下没有用户
)}
)}
)}
) })