feat: 实现LDAP创建OU和用户(LDAP Add)+弹框风格统一

This commit is contained in:
logan committed 2026-05-30 10:24:29 +08:00
1 parent 7b5f95e702
commit c51f619a7d
2 files changed
+167 -49

No files matched your search

+52 -47
View File
@@ -6,7 +6,7 @@ import { toast } from '@/hooks/useToast';
import {
Save, ChevronRight, ChevronDown, RefreshCw,
CheckCircle, XCircle, Trash2, KeyRound, FolderOpen, Users,
Shield, AlertTriangle, Import,
Shield, AlertTriangle, Import, UserPlus,
} from 'lucide-react';
/* ── types ── */
@@ -613,19 +613,22 @@ export default function LdapConfigPage() {
{/* ── 创建 OU 弹框 ── */}
{showCreateOu && (
<ModalOverlay open={true} onClose={() => setShowCreateOu(false)} width="w-[460px]">
<div className="bg-[var(--dg-bg-secondary)] rounded-xl shadow-2xl">
<div className="flex items-center justify-between px-5 py-4">
<h3 className="text-sm font-semibold">新建组织单元 (OU)</h3>
<button onClick={() => setShowCreateOu(false)} className="text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)]">✕</button>
<ModalOverlay open={true} onClose={() => setShowCreateOu(false)} width="max-w-md">
<div className="w-full bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-xl p-5 shadow-xl">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<FolderOpen className="w-4 h-4 text-blue-400" />
<h3 className="text-sm font-semibold text-[var(--dg-text-primary)]">新建组织单元 (OU)</h3>
</div>
<button onClick={() => setShowCreateOu(false)} className="text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)] transition-colors">✕</button>
</div>
<div className="px-5 space-y-4">
<div className="space-y-3 mb-4">
<div>
<label className="text-xs text-[var(--dg-text-muted)] mb-1 block">OU 名称 *</label>
<input
value={newOuName}
onChange={e => setNewOuName(e.target.value)}
className="w-full px-3 py-2 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400"
className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400 transition-colors"
placeholder="如: devops, engineering"
/>
</div>
@@ -634,7 +637,7 @@ export default function LdapConfigPage() {
<select
value={newOuParent}
onChange={e => setNewOuParent(e.target.value)}
className="w-full px-3 py-2 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg text-sm"
className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400 transition-colors"
>
<option value="">根: {config.base_dn}</option>
{ouList.map(ou => <option key={ou.dn} value={ou.dn}>{ou.dn}</option>)}
@@ -645,17 +648,17 @@ export default function LdapConfigPage() {
<input
value={newOuDesc}
onChange={e => setNewOuDesc(e.target.value)}
className="w-full px-3 py-2 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400"
className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400 transition-colors"
placeholder="可选,如: 研发团队"
/>
</div>
</div>
<div className="flex justify-end gap-3 px-5 py-4">
<button onClick={() => setShowCreateOu(false)} className="px-4 py-2 text-sm text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)]">取消</button>
<div className="flex justify-end gap-2">
<button onClick={() => setShowCreateOu(false)} className="px-4 py-2 text-sm text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)] transition-colors">取消</button>
<button
onClick={handleCreateOu}
disabled={creatingOu || !newOuName.trim()}
className="px-4 py-2 text-sm font-medium rounded-lg bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50"
className="px-4 py-2 text-sm font-medium rounded-lg bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50 transition-colors"
>
{creatingOu ? '创建中…' : '创建 OU'}
</button>
@@ -666,41 +669,42 @@ export default function LdapConfigPage() {
{/* ── 添加用户弹框 ── */}
{showAddUser && (
<ModalOverlay open={true} onClose={() => setShowAddUser(false)} width="w-[500px]">
<div className="bg-[var(--dg-bg-secondary)] rounded-xl shadow-2xl">
<div className="flex items-center justify-between px-5 py-4">
<h3 className="text-sm font-semibold">添加用户到 LDAP</h3>
<button onClick={() => setShowAddUser(false)} className="text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)]">✕</button>
</div>
<div className="px-5 space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-xs text-[var(--dg-text-muted)] mb-1 block">用户名 *</label>
<input
value={addUsername}
onChange={e => setAddUsername(e.target.value)}
className="w-full px-3 py-2 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400"
placeholder="登录名,如 zhangsan"
/>
</div>
<div>
<label className="text-xs text-[var(--dg-text-muted)] mb-1 block">密码 *</label>
<input
type="password"
value={addPassword}
onChange={e => setAddPassword(e.target.value)}
className="w-full px-3 py-2 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400"
placeholder="初始密码"
/>
</div>
<ModalOverlay open={true} onClose={() => setShowAddUser(false)} width="max-w-md">
<div className="w-full bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-xl p-5 shadow-xl">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<UserPlus className="w-4 h-4 text-emerald-400" />
<h3 className="text-sm font-semibold text-[var(--dg-text-primary)]">添加用户到 LDAP</h3>
</div>
<div className="grid grid-cols-2 gap-4">
<button onClick={() => setShowAddUser(false)} className="text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)] transition-colors">✕</button>
</div>
<div className="space-y-3 mb-4">
<div>
<label className="text-xs text-[var(--dg-text-muted)] mb-1 block">用户名 *</label>
<input
value={addUsername}
onChange={e => setAddUsername(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:outline-none focus:border-blue-400 transition-colors"
placeholder="登录名,如 zhangsan"
/>
</div>
<div>
<label className="text-xs text-[var(--dg-text-muted)] mb-1 block">密码 *</label>
<input
type="password"
value={addPassword}
onChange={e => setAddPassword(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:outline-none focus:border-blue-400 transition-colors"
placeholder="初始密码"
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-[var(--dg-text-muted)] mb-1 block">显示名称</label>
<input
value={addDisplayName}
onChange={e => setAddDisplayName(e.target.value)}
className="w-full px-3 py-2 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400"
className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400 transition-colors"
placeholder="如: 张三"
/>
</div>
@@ -709,7 +713,7 @@ export default function LdapConfigPage() {
<input
value={addEmail}
onChange={e => setAddEmail(e.target.value)}
className="w-full px-3 py-2 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400"
className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400 transition-colors"
placeholder="可选"
/>
</div>
@@ -719,20 +723,21 @@ export default function LdapConfigPage() {
<select
value={addOuDn}
onChange={e => setAddOuDn(e.target.value)}
className="w-full px-3 py-2 bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-lg text-sm"
className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:outline-none focus:border-blue-400 transition-colors"
>
<option value="">根: {config.base_dn}</option>
{ouList.map(ou => <option key={ou.dn} value={ou.dn}>{ou.name} ({ou.dn})</option>)}
</select>
</div>
</div>
<div className="flex justify-end gap-3 px-5 py-4">
<button onClick={() => setShowAddUser(false)} className="px-4 py-2 text-sm text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)]">取消</button>
<div className="flex justify-end gap-2">
<button onClick={() => setShowAddUser(false)} className="px-4 py-2 text-sm text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)] transition-colors">取消</button>
<button
onClick={handleAddUser}
disabled={addingUser || !addUsername.trim() || !addPassword.trim()}
className="px-4 py-2 text-sm font-medium rounded-lg bg-emerald-500 text-white hover:bg-emerald-600 disabled:opacity-50"
className="flex items-center gap-1.5 px-4 py-2 text-sm font-medium rounded-lg bg-emerald-500 text-white hover:bg-emerald-600 disabled:opacity-50 transition-colors"
>
<UserPlus className="w-3.5 h-3.5" />
{addingUser ? '创建中…' : '创建用户'}
</button>
</div>