import { useEffect, useMemo, useState } from 'react'; import { Alert, Button, Card, Checkbox, Col, Form, Row, Select, Space, Tabs, Tag, Typography, message, } from 'antd'; import { HQ_ADMIN_ROLES, HQ_PERMISSION_CATALOG, type HqPermissionKey, } from '@dukang/shared-types'; import { request, type HqProfile } from '../lib/api'; type RolePermRes = { role: string; permissionKeys: string[] }; type AccountOption = { id: string; name: string; phone: string; loginName: string | null; adminRole: string }; type AccountPermRes = { account: AccountOption; permissionKeys: string[]; rolePermissionKeys: string[]; userPermissionKeys: string[]; effectivePermissionKeys: string[]; }; const ROLE_LABELS = Object.fromEntries(HQ_ADMIN_ROLES.map((r) => [r.value, r.label])); function PermissionChecklist({ value, onChange, disabled, }: { value: string[]; onChange: (keys: string[]) => void; disabled?: boolean; }) { return ( onChange(checked as string[])} > {HQ_PERMISSION_CATALOG.map((item) => ( {item.label} ))} ); } export default function HqPermissionsPage() { const [profile, setProfile] = useState(null); const [role, setRole] = useState('OPS'); const [roleKeys, setRoleKeys] = useState([]); const [roleLoading, setRoleLoading] = useState(false); const [roleSaving, setRoleSaving] = useState(false); const [accounts, setAccounts] = useState([]); const [accountId, setAccountId] = useState(); const [accountKeys, setAccountKeys] = useState([]); const [roleInheritedKeys, setRoleInheritedKeys] = useState([]); const [accountLoading, setAccountLoading] = useState(false); const [accountSaving, setAccountSaving] = useState(false); const previewEffectiveKeys = useMemo( () => [...new Set([...roleInheritedKeys, ...accountKeys])], [roleInheritedKeys, accountKeys], ); const isSuperAdmin = profile?.adminRole === 'SUPER_ADMIN'; useEffect(() => { request('/admin/auth/me').then(setProfile).catch(() => {}); }, []); useEffect(() => { if (!isSuperAdmin) return; request<{ items: AccountOption[] }>('/admin/hq-accounts?page=1&pageSize=100') .then((res) => setAccounts(res.items)) .catch(() => {}); }, [isSuperAdmin]); useEffect(() => { if (!isSuperAdmin || !role) return; setRoleLoading(true); request(`/admin/hq-permissions/roles/${role}`) .then((res) => setRoleKeys(res.permissionKeys)) .finally(() => setRoleLoading(false)); }, [isSuperAdmin, role]); useEffect(() => { if (!isSuperAdmin || !accountId) return; setAccountLoading(true); request(`/admin/hq-permissions/accounts/${accountId}`) .then((res) => { setAccountKeys(res.userPermissionKeys); setRoleInheritedKeys(res.rolePermissionKeys); }) .finally(() => setAccountLoading(false)); }, [isSuperAdmin, accountId]); async function saveRolePermissions() { setRoleSaving(true); try { const res = await request(`/admin/hq-permissions/roles/${role}`, { method: 'PUT', body: JSON.stringify({ permissionKeys: roleKeys }), }); setRoleKeys(res.permissionKeys); message.success('角色权限已保存'); } catch (e) { message.error(e instanceof Error ? e.message : '保存失败'); } finally { setRoleSaving(false); } } async function saveAccountPermissions() { if (!accountId) return; setAccountSaving(true); try { const res = await request(`/admin/hq-permissions/accounts/${accountId}`, { method: 'PUT', body: JSON.stringify({ permissionKeys: accountKeys }), }); setAccountKeys(res.userPermissionKeys); setRoleInheritedKeys(res.rolePermissionKeys); message.success('用户权限已保存'); } catch (e) { message.error(e instanceof Error ? e.message : '保存失败'); } finally { setAccountSaving(false); } } if (!isSuperAdmin) { return (
权限分配
); } return (
权限分配 按角色配置基础权限;按用户可追加专属权限。最终生效权限 = 角色权限 ∪ 用户权限(超级管理员始终拥有全部权限)。
a.adminRole !== 'SUPER_ADMIN') .map((a) => ({ value: a.id, label: `${a.name} · ${a.loginName || a.phone} · ${ROLE_LABELS[a.adminRole] || a.adminRole}`, }))} />
{!accountId ? ( ) : ( <> 角色继承: {roleInheritedKeys.map((key) => { const item = HQ_PERMISSION_CATALOG.find((p) => p.key === key); return ( {item?.label || key} ); })} 下方勾选为用户专属追加权限(保存后与角色权限合并生效)。 合并生效: {previewEffectiveKeys.map((key) => { const item = HQ_PERMISSION_CATALOG.find((p) => p.key === (key as HqPermissionKey)); return {item?.label || key}; })}
)} ), }, ]} />
); }