diff --git a/src/app/admin/create-user/role-manager.tsx b/src/app/admin/create-user/role-manager.tsx index 6fe5301..a5c2c12 100644 --- a/src/app/admin/create-user/role-manager.tsx +++ b/src/app/admin/create-user/role-manager.tsx @@ -33,6 +33,7 @@ export default function RoleManager({ setResult, onUserUpdated }: Props) { const [editingDisplayName, setEditingDisplayName] = useState(null) const [editDisplayNameValue, setEditDisplayNameValue] = useState('') const [savingDisplayName, setSavingDisplayName] = useState(false) + const [syncing, setSyncing] = useState(null) // username being synced, or 'all' const fetchRoleData = useCallback(async () => { try { @@ -99,6 +100,23 @@ export default function RoleManager({ setResult, onUserUpdated }: Props) { setPending({}); if (fail === 0) fetchRoleData(); setSaving(false) } + async function syncUsers(usernames: string[], targetSite?: string) { + setSyncing(usernames.length === 1 ? usernames[0] : 'all') + try { + const res = await fetch('/api/admin/sync-users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ usernames, targetSite }) }) + const d = await res.json() + if (res.ok) { + fetchRoleData() + const failed = Object.entries(d.results || {}).flatMap(([site, users]: [string, any]) => + Object.entries(users).filter(([, ok]) => !ok).map(([u]) => `${u}→${site}`) + ) + setResult(failed.length === 0, failed.length > 0 ? `${failed.join(', ')} 同步失败` : '同步完成') + } + else setResult(false, d.error || '同步失败') + } catch { setResult(false, '网络错误') } + finally { setSyncing(null) } + } + if (!roleData) return

加载中...

const userMap = new Map() @@ -112,9 +130,14 @@ export default function RoleManager({ setResult, onUserUpdated }: Props) {

修改角色后点击「保存修改」统一提交{changed > 0 && ({changed} 项待保存)}

- +
+ + +
@@ -161,9 +184,9 @@ export default function RoleManager({ setResult, onUserUpdated }: Props) { )} - - - + + + ))} @@ -172,17 +195,27 @@ export default function RoleManager({ setResult, onUserUpdated }: Props) { ) } -function RoleCell({ site, username, originalRole, roles, pending, onSelect, getCurrentRole }: { +function RoleCell({ site, username, originalRole, roles, pending, onSelect, getCurrentRole, syncing, onSync }: { site: string; username: string; originalRole: string; roles: string[] pending: Record onSelect: (site: string, username: string, newRole: string, originalRole: string) => void getCurrentRole: (site: string, username: string, originalRole: string) => string + syncing: string | null; onSync: (usernames: string[], site?: string) => void }) { const isReserved = username === 'admin' || username === 'localadmin' const changed = !!pending[`${site}:${username}`] const currentRole = getCurrentRole(site, username, originalRole) if (isReserved) return {currentRole} - if (originalRole === '—' && !changed) return 未同步 + if (originalRole === '—' && !changed) return ( + + 未同步 + {' '} + + + ) return (