- setNewOuName(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="例如:研发部、市场部" />
+ setNewOuName(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="例如:研发部、市场部"
+ />
- setNewOuDesc(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="可选描述" />
+ setNewOuDesc(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="可选描述"
+ />
-
-
diff --git a/frontend/src/pages/LdapConfigPage.tsx b/frontend/src/pages/LdapConfigPage.tsx
index 42921db..b5b1953 100644
--- a/frontend/src/pages/LdapConfigPage.tsx
+++ b/frontend/src/pages/LdapConfigPage.tsx
@@ -3,9 +3,13 @@ import api from '@/api/request';
import { ConfirmDialog } from '@/components/ConfirmDialog';
import { toast } from '@/hooks/useToast';
import {
- type LdapConfigData, type OuItem, type LdapUser,
- type LdapTestResult, type LdapAccountTestResult,
- type CreateOuFormState, type AddUserFormState,
+ type LdapConfigData,
+ type OuItem,
+ type LdapUser,
+ type LdapTestResult,
+ type LdapAccountTestResult,
+ type CreateOuFormState,
+ type AddUserFormState,
defaultLdapConfig,
} from '@/features/system/ldap/types';
import { LdapConfigForm } from '@/features/system/ldap/components/LdapConfigForm';
@@ -20,6 +24,8 @@ import { LdapAccountTestDialog } from '@/features/system/ldap/components/LdapAcc
*/
export default function LdapConfigPage() {
const [config, setConfig] = useState
(defaultLdapConfig);
+ /** 后端已存在已保存的 LDAP 配置(账号管理/导入入口的门控依据) */
+ const [configured, setConfigured] = useState(false);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [hasStoredPwd, setHasStoredPwd] = useState(false);
@@ -52,7 +58,13 @@ export default function LdapConfigPage() {
const [showAddUser, setShowAddUser] = useState(false);
const [addingUser, setAddingUser] = useState(false);
- const [addUserForm, setAddUserForm] = useState({ username: '', displayName: '', email: '', password: '', ouDn: '' });
+ const [addUserForm, setAddUserForm] = useState({
+ username: '',
+ displayName: '',
+ email: '',
+ password: '',
+ ouDn: '',
+ });
const [deleting, setDeleting] = useState(false);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
@@ -61,9 +73,10 @@ export default function LdapConfigPage() {
const fetchConfig = useCallback(async () => {
try {
setLoading(true);
- const res = await api.get('/ldap-config') as any;
+ const res = (await api.get('/ldap-config')) as any;
const data = res?.data ?? res;
if (data && typeof data === 'object' && data.ldap_host) {
+ setConfigured(true);
setConfig({
ldap_host: data.ldap_host || '',
ldap_port: data.ldap_port || 389,
@@ -72,9 +85,10 @@ export default function LdapConfigPage() {
admin_password: '',
search_filter: data.search_filter || defaultLdapConfig.search_filter,
base_dn: data.base_dn || '',
- user_mapping: typeof data.user_mapping === 'string'
- ? JSON.parse(data.user_mapping)
- : data.user_mapping || defaultLdapConfig.user_mapping,
+ user_mapping:
+ typeof data.user_mapping === 'string'
+ ? JSON.parse(data.user_mapping)
+ : data.user_mapping || defaultLdapConfig.user_mapping,
});
setHasStoredPwd(!!data.admin_password);
}
@@ -85,7 +99,9 @@ export default function LdapConfigPage() {
}
}, []);
- useEffect(() => { fetchConfig(); }, [fetchConfig]);
+ useEffect(() => {
+ fetchConfig();
+ }, [fetchConfig]);
/* ── 测试服务器连接(先测后存) ── */
const handleTestServer = async () => {
@@ -101,10 +117,14 @@ export default function LdapConfigPage() {
if (config.admin_password) setHasStoredPwd(true);
// 测试连接
- const res = await api.post('/ldap-config/test', {
- test_user: config.admin_dn,
- test_password: config.admin_password || '__use_stored__',
- }, { timeout: 10000 }) as any;
+ const res = (await api.post(
+ '/ldap-config/test',
+ {
+ test_user: config.admin_dn,
+ test_password: config.admin_password || '__use_stored__',
+ },
+ { timeout: 10000 },
+ )) as any;
const data = res?.data ?? res;
setTestResult({ ok: true, msg: data?.message || 'LDAP 服务器连接成功' });
setServerConnected(true);
@@ -127,6 +147,7 @@ export default function LdapConfigPage() {
}
await api.put('/ldap-config', payload);
if (config.admin_password) setHasStoredPwd(true);
+ setConfigured(true);
toast('success', 'LDAP 配置保存成功');
} catch (e: any) {
toast('error', e?.response?.data?.detail || '保存失败');
@@ -140,10 +161,14 @@ export default function LdapConfigPage() {
setTestingAccount(true);
setAccountResult(null);
try {
- const res = await api.post('/ldap-config/test', {
- test_user: testUser,
- test_password: testPass,
- }, { timeout: 10000 }) as any;
+ const res = (await api.post(
+ '/ldap-config/test',
+ {
+ test_user: testUser,
+ test_password: testPass,
+ },
+ { timeout: 10000 },
+ )) as any;
const data = res?.data ?? res;
if (data?.success === false) {
setAccountResult({ ok: false, msg: data?.message || '验证失败' });
@@ -161,7 +186,7 @@ export default function LdapConfigPage() {
const fetchOus = async () => {
setLoadingOu(true);
try {
- const res = await api.post('/ldap-config/ou-list', {}) as any;
+ const res = (await api.post('/ldap-config/ou-list', {})) as any;
const data = res?.data ?? res;
setOuList(Array.isArray(data?.ous) ? data.ous : Array.isArray(data) ? data : []);
} catch {
@@ -177,7 +202,7 @@ export default function LdapConfigPage() {
setOuUsers([]);
setSelectedDns(new Set());
try {
- const res = await api.post('/ldap-config/user-list', { ou_dn: ou }) as any;
+ const res = (await api.post('/ldap-config/user-list', { ou_dn: ou })) as any;
const data = res?.data ?? res;
setOuUsers(Array.isArray(data?.users) ? data.users : Array.isArray(data) ? data : []);
} catch {
@@ -241,7 +266,8 @@ export default function LdapConfigPage() {
const confirmDelete = async () => {
setShowDeleteConfirm(false);
setDeleting(true);
- let ok = 0, fail = 0;
+ let ok = 0,
+ fail = 0;
for (const dn of selectedDns) {
try {
await api.post('/ldap-config/delete-user', { dn });
@@ -269,14 +295,12 @@ export default function LdapConfigPage() {
/* 打开添加用户弹框:预填当前 OU(与原行为一致) */
const openAddUser = () => {
- setAddUserForm(prev => ({ ...prev, ouDn: selectedOu || config.base_dn }));
+ setAddUserForm((prev) => ({ ...prev, ouDn: selectedOu || config.base_dn }));
setShowAddUser(true);
};
- const patchCreateOuForm = (patch: Partial) =>
- setCreateOuForm(prev => ({ ...prev, ...patch }));
- const patchAddUserForm = (patch: Partial) =>
- setAddUserForm(prev => ({ ...prev, ...patch }));
+ const patchCreateOuForm = (patch: Partial) => setCreateOuForm((prev) => ({ ...prev, ...patch }));
+ const patchAddUserForm = (patch: Partial) => setAddUserForm((prev) => ({ ...prev, ...patch }));
/* 账号测试:输入变更清空上次结果,关闭时清空结果但保留凭据 */
const handleTestUserChange = (value: string) => {
@@ -316,8 +340,13 @@ export default function LdapConfigPage() {
onOpenAccountTest={() => setShowAccountTest(true)}
/>
- {/* ── LDAP 账号管理(折叠区,连接成功后显示) ── */}
- {config.ldap_host && config.base_dn && (
+ {/* ── LDAP 账号管理(未保存配置前隐藏,保存后出现) ── */}
+ {!loading && !configured && (
+
+ LDAP 未启用:保存基础连接配置后,这里会出现 LDAP 账号管理与用户导入入口
+
+ )}
+ {configured && (
{ confirmDelete(); }}
+ onConfirm={() => {
+ confirmDelete();
+ }}
onCancel={() => setShowDeleteConfirm(false)}
/>