"use client" import * as React from "react" import { useQuery, useMutation, gql } from '@apollo/client'; import { IconUsers, IconShield, IconPlus, IconMinus, IconUserCheck, IconChevronDown, } from "@tabler/icons-react" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Checkbox } from "@/components/ui/checkbox" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Avatar, AvatarFallback } from "@/components/ui/avatar" import { toast } from "sonner" const GET_USERS_WITH_GROUPS = gql` query GetUsersWithGroups($offset: Int, $limit: Int, $sortBy: String, $sortOrder: String, $filter: String) { usersWithGroups(offset: $offset, limit: $limit, sortBy: $sortBy, sortOrder: $sortOrder, filter: $filter) { user { id username email isActivate createdAt updatedAt } groups } } ` const GET_ALL_ROLES = gql` query GetAllRoles { roles(pagination: { page: 1, perPage: 100 }) { items { id name code level isActive } } } ` const ASSIGN_ROLE_TO_USER = gql` mutation AssignRoleToUser($userId: UUID!, $roleName: String!) { assignRoleToUser(userId: $userId, roleName: $roleName) } ` const REMOVE_ROLE_FROM_USER = gql` mutation RemoveRoleFromUser($userId: UUID!, $roleName: String!) { removeRoleFromUser(userId: $userId, roleName: $roleName) } ` const BATCH_ASSIGN_ROLES_TO_USER = gql` mutation BatchAssignRolesToUser($userId: UUID!, $roleIds: [UUID!]!) { batchAssignRolesToUser(userId: $userId, roleIds: $roleIds) } ` interface UserWithGroups { user: User groups: string[] } interface User { id: string username: string email: string isActivate: boolean createdAt: string updatedAt: string groups: string[] } interface Role { id: string name: string code: string level: number isActive: boolean } function UserRoleCard({ user, allRoles, onRoleChange }: { user: User allRoles: Role[] onRoleChange: () => void }) { const [assignRole] = useMutation(ASSIGN_ROLE_TO_USER) const [removeRole] = useMutation(REMOVE_ROLE_FROM_USER) const [open, setOpen] = React.useState(false) const availableRoles = allRoles.filter(role => role.isActive && !user.groups.some(group => group.toLowerCase() === role.code.toLowerCase()) ) const handleAssignRole = async (roleId: string) => { const role = allRoles.find((r: Role) => r.id === roleId) if (!role) return try { await assignRole({ variables: { userId: user.id, roleName: role.code } }) toast.success("角色分配成功") onRoleChange() } catch (error) { toast.error("角色分配失败") } } const handleRemoveRole = async (roleName: string) => { const role = allRoles.find((r: Role) => r.code === roleName) if (!role) return try { await removeRole({ variables: { userId: user.id, roleName: role.code } }) toast.success("角色移除成功") onRoleChange() } catch (error) { toast.error("角色移除失败") } } const getInitials = (username: string) => { return username.slice(0, 2).toUpperCase() } return (
{getInitials(user.username)}
{user.username} {user.email}
{user.isActivate ? "活跃" : "非活跃"}
{availableRoles.length === 0 ? (
没有可分配的角色
) : (
可分配的角色
{availableRoles.map((role) => ( ))}
)}
{user.groups.length === 0 ? (
未分配任何角色
) : ( user.groups.map((groupCode) => { const role = allRoles.find((r: Role) => r.code.toLowerCase() === groupCode.toLowerCase()) return (
{role?.name || groupCode}
{groupCode}
{role ? `Level ${role.level}` : "角色"}
) }) )}
) } function BatchAssignDialog({ selectedUsers, allRoles, onSuccess }: { selectedUsers: User[] allRoles: Role[] onSuccess: () => void }) { const [open, setOpen] = React.useState(false) const [selectedRole, setSelectedRole] = React.useState("") const [loading, setLoading] = React.useState(false) const [batchAssignRoles] = useMutation(BATCH_ASSIGN_ROLES_TO_USER) const handleBatchAssign = async () => { if (!selectedRole || selectedUsers.length === 0) { toast.error("请选择角色和用户") return } const role = allRoles.find((r: Role) => r.id === selectedRole) if (!role) return setLoading(true) try { await Promise.all( selectedUsers.map(user => batchAssignRoles({ variables: { userId: user.id, roleIds: [role.id] } }) ) ) toast.success(`成功为 ${selectedUsers.length} 个用户分配角色`) setOpen(false) setSelectedRole("") onSuccess() } catch (error) { toast.error("批量分配失败") } finally { setLoading(false) } } return ( 批量分配角色 为选中的 {selectedUsers.length} 个用户分配角色
{selectedUsers.map((user) => (
{user.username.slice(0, 2).toUpperCase()} {user.username} ({user.email})
))}
) } export function UserRoleManagement() { const [searchTerm, setSearchTerm] = React.useState("") const [roleFilter, setRoleFilter] = React.useState("") const [selectedUsers, setSelectedUsers] = React.useState([]) const [pagination, setPagination] = React.useState({ offset: 0, limit: 12, }) const { data: usersData, loading: usersLoading, refetch: refetchUsers } = useQuery(GET_USERS_WITH_GROUPS, { variables: { offset: pagination.offset, limit: pagination.limit, sortBy: "created_at", sortOrder: "DESC", filter: searchTerm || undefined, }, fetchPolicy: 'cache-and-network' }) // 当搜索词或角色筛选改变时,重新获取数据 React.useEffect(() => { refetchUsers() }, [searchTerm, refetchUsers]) const { data: rolesData } = useQuery(GET_ALL_ROLES, { fetchPolicy: 'cache-and-network' }) const rawUsersWithGroups = usersData?.usersWithGroups || [] const users: User[] = rawUsersWithGroups.map((userWithGroups: UserWithGroups) => ({ ...userWithGroups.user, groups: userWithGroups.groups })) const allRoles = rolesData?.roles?.items || [] const totalUsers = users.length const handleUserSelect = (user: User, checked: boolean) => { if (checked) { setSelectedUsers(prev => [...prev, user]) } else { setSelectedUsers(prev => prev.filter(u => u.id !== user.id)) } } const handleSelectAll = (checked: boolean) => { if (checked) { setSelectedUsers(filteredUsers) } else { setSelectedUsers([]) } } // 按角色筛选用户 const filteredUsers = users.filter(user => { if (!roleFilter) return true const selectedRole = allRoles.find((r: Role) => r.id === roleFilter) return selectedRole ? user.groups.some(group => group.toLowerCase() === selectedRole.code.toLowerCase()) : true }) return (
用户角色管理 为用户分配和管理角色,支持单个和批量操作 {roleFilter && ` • 筛选角色: ${allRoles.find((r: Role) => r.id === roleFilter)?.name}`} {searchTerm && ` • 搜索: "${searchTerm}"`}
setSearchTerm(e.target.value)} className="max-w-sm" />
{(searchTerm || roleFilter) && ( )}
0} onCheckedChange={(checked) => handleSelectAll(!!checked)} />
{ refetchUsers() setSelectedUsers([]) }} />
{usersLoading ? (
{[...Array(6)].map((_, i) => (
))}
) : filteredUsers.length === 0 ? (

暂无用户

没有找到匹配的用户。

) : (
{filteredUsers.map((user) => (
u.id === user.id)} onCheckedChange={(checked) => handleUserSelect(user, !!checked)} />
))}
)} {totalUsers >= pagination.limit && (
显示 {pagination.offset + 1} - {pagination.offset + users.length} 项
)}
) }