'use client'; import * as React from 'react'; import { useState, useRef } from 'react'; import { Button } from '@nice/ui/components/button'; import { Popover, PopoverContent, PopoverTrigger } from '@nice/ui/components/popover'; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@nice/ui/components/command'; import { IconChevronDown, IconCheck, IconShield, IconX } from '@tabler/icons-react'; import { cn } from '@nice/ui/lib/utils'; import { useTRPC } from '@fenghuo/client'; import { useQuery } from '@tanstack/react-query'; import { Badge } from '@nice/ui/components/badge'; // 角色接口定义 export interface Role { id: string; name: string; slug: string; description?: string | null; permissions: string[]; isSystem: boolean; isActive: boolean; createdAt: Date; updatedAt: Date; } // 角色选择器属性 interface RoleSelectorProps { value?: string | string[]; // 支持单选和多选 onValueChange?: (value: string | string[]) => void; placeholder?: string; className?: string; disabled?: boolean; allowClear?: boolean; multiple?: boolean; // 是否支持多选 showDescription?: boolean; // 是否显示角色描述 showBadge?: boolean; // 是否显示系统角色标识 includeInactive?: boolean; // 是否包含非活跃角色 modal?: boolean; // 是否为模态模式,用于在 Dialog 中解决滚轮问题 } // 角色项组件 interface RoleItemProps { role: Role; isSelected: boolean; showDescription?: boolean; showBadge?: boolean; onSelect: () => void; } function RoleItem({ role, isSelected, showDescription = true, showBadge = true, onSelect }: RoleItemProps) { return ( {/* 角色图标 */} {/* 角色信息 */} {role.name} {showBadge && role.isSystem && ( 系统 )} {showBadge && !role.isActive && ( 已禁用 )} {showDescription && role.description && ( {role.description} )} {/* 选中状态指示 */} {isSelected && } ); } // 主组件 export function RoleSelect({ value, onValueChange, placeholder = '选择角色', className, disabled = false, allowClear = true, multiple = false, showDescription = true, showBadge = true, includeInactive = false, modal = false, }: RoleSelectorProps) { const [open, setOpen] = useState(false); const [searchValue, setSearchValue] = useState(''); const containerRef = useRef(null); const trpc = useTRPC(); // 使用 tRPC 获取角色列表 const { data: roleData, isLoading, error, } = useQuery({ ...trpc.role.findManyWithPagination.queryOptions({ page: 1, pageSize: 100, // 获取足够多的角色 where: includeInactive ? undefined : { isActive: true }, // 根据配置过滤活跃角色 }), }); const roles = roleData?.items || []; // 过滤角色(根据搜索关键词) const filteredRoles = React.useMemo(() => { if (!searchValue.trim()) { return roles; } const searchTerm = searchValue.toLowerCase(); return roles.filter( (role) => role.name.toLowerCase().includes(searchTerm) || role.description?.toLowerCase().includes(searchTerm) || role.slug.toLowerCase().includes(searchTerm), ); }, [roles, searchValue]); // 获取选中的角色 const selectedRoles = React.useMemo(() => { if (!value) return []; const selectedIds = Array.isArray(value) ? value : [value]; return roles.filter((role) => selectedIds.includes(role.id)); }, [roles, value]); // 处理选择逻辑 const handleSelect = (roleId: string) => { if (!onValueChange) return; if (multiple) { const currentValues = Array.isArray(value) ? value : value ? [value] : []; if (currentValues.includes(roleId)) { // 取消选择 const newValues = currentValues.filter((id) => id !== roleId); onValueChange(newValues); } else { // 添加选择 onValueChange([...currentValues, roleId]); } } else { // 单选模式 onValueChange(roleId); setOpen(false); } }; // 清除选择 const handleClear = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); onValueChange?.(multiple ? [] : ''); }; // 处理单个角色移除(仅多选模式) const handleRemoveRole = (roleId: string, e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (multiple) { const currentValues = Array.isArray(value) ? value : value ? [value] : []; const newValues = currentValues.filter((id) => id !== roleId); onValueChange?.(newValues); } }; // 渲染触发器内容 const renderTriggerContent = () => { if (selectedRoles.length === 0) { return placeholder; } if (multiple) { if (selectedRoles.length === 1) { return selectedRoles[0]!.name; } else { return ( {selectedRoles.slice(0, 2).map((role) => ( {role.name} handleRemoveRole(role.id, e)} className="ml-1 hover:bg-muted-foreground/20 rounded-sm p-0.5 cursor-pointer inline-flex items-center justify-center" role="button" tabIndex={0} aria-label={`移除角色 ${role.name}`} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleRemoveRole(role.id, e as any); } }} > ))} {selectedRoles.length > 2 && ( +{selectedRoles.length - 2} )} ); } } return selectedRoles[0]!.name; }; // 检查是否选中 const isSelected = (roleId: string) => { if (!value) return false; return Array.isArray(value) ? value.includes(roleId) : value === roleId; }; if (error) { console.error('加载角色列表失败:', error); } return ( {renderTriggerContent()} {allowClear && selectedRoles.length > 0 && ( { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClear(e as any); } }} > )} {isLoading ? ( 加载中... ) : filteredRoles.length === 0 ? ( 未找到角色 ) : ( {/* 多选模式下显示已选择数量 */} {multiple && selectedRoles.length > 0 && ( 已选择 {selectedRoles.length} 个角色 )} {filteredRoles.map((role) => ( handleSelect(role.id)} /> ))} )} ); } // 导出便捷的单选和多选组件 export function SingleRoleSelector( props: Omit & { onValueChange?: (value: string) => void; }, ) { const handleValueChange = (value: string | string[]) => { if (props.onValueChange && typeof value === 'string') { props.onValueChange(value); } }; return ; } export function MultipleRoleSelector( props: Omit & { onValueChange?: (value: string[]) => void; }, ) { const handleValueChange = (value: string | string[]) => { if (props.onValueChange && Array.isArray(value)) { props.onValueChange(value); } }; return ; }
{role.description}