'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, IconBuilding, 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'; import { TaxonomySlug } from '@fenghuo/common'; // 站点接口定义 export interface Station { id: string; name: string; slug: string | null; description: string | null; displayName: string; // 格式化后的显示名称:父组织/当前组织 parentId: string | null; parent: { id: string; name: string; slug: string | null; } | null; terms: Array<{ id: string; name: string; slug: string; taxonomy: { id: string; name: string; slug: string; }; }>; createdAt: Date; updatedAt: Date; } // 站点选择器属性 interface StationSelectorProps { 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 StationItemProps { station: Station; isSelected: boolean; showDescription?: boolean; showBadge?: boolean; onSelect: () => void; } function StationItem({ station, isSelected, showDescription = true, showBadge = true, onSelect }: StationItemProps) { // 提取组织类型术语(除了 station 之外的其他类型) const organizationTypeTerms = station.terms?.filter( (term) => term.taxonomy?.slug === TaxonomySlug.ORGANIZATION_TYPE && term.slug !== 'station' ) || []; return ( {/* 站点图标 */} {/* 站点信息 */} {station.displayName} {showBadge && organizationTypeTerms.map((term) => ( {term.name} ))} {showDescription && station.description && ( {station.description} )} {/* 选中状态指示 */} {isSelected && } ); } // 主组件 export function StationSelect({ value, onValueChange, placeholder = '选择站点', className, disabled = false, allowClear = true, multiple = false, showDescription = true, showBadge = true, includeInactive = false, modal = false, }: StationSelectorProps) { const [open, setOpen] = useState(false); const [searchValue, setSearchValue] = useState(''); const containerRef = useRef(null); const trpc = useTRPC(); // 使用 tRPC 获取组织列表,过滤出站点类型的组织 const { data: organizationData, isLoading, error, } = useQuery({ ...trpc.organization.findMany.queryOptions({ where: { deletedAt: includeInactive ? undefined : null, terms: { some: { slug: 'station', taxonomy: { slug: TaxonomySlug.ORGANIZATION_TYPE, }, }, }, }, include: { parent: { select: { id: true, name: true, slug: true, }, }, terms: { include: { taxonomy: true, }, }, }, orderBy: [ { level: 'asc' }, { order: 'asc' }, ], }), }); // 转换为站点数据格式 const stations = React.useMemo(() => { if (!organizationData) return []; return organizationData.map((org: any): Station => ({ id: org.id, name: org.name, slug: org.slug, description: org.description, displayName: org.parent ? `${org.parent.name}/${org.name}` : org.name, parentId: org.parentId, parent: org.parent, terms: org.terms || [], createdAt: org.createdAt, updatedAt: org.updatedAt, })); }, [organizationData]); // 过滤站点(根据搜索关键词) const filteredStations = React.useMemo(() => { if (!searchValue.trim()) { return stations; } const searchTerm = searchValue.toLowerCase(); return stations.filter( (station) => station.name.toLowerCase().includes(searchTerm) || station.displayName.toLowerCase().includes(searchTerm) || station.description?.toLowerCase().includes(searchTerm) || station.slug?.toLowerCase().includes(searchTerm), ); }, [stations, searchValue]); // 获取选中的站点 const selectedStations = React.useMemo(() => { if (!value) return []; const selectedIds = Array.isArray(value) ? value : [value]; return stations.filter((station) => selectedIds.includes(station.id)); }, [stations, value]); // 处理选择逻辑 const handleSelect = (stationId: string) => { if (!onValueChange) return; if (multiple) { const currentValues = Array.isArray(value) ? value : value ? [value] : []; if (currentValues.includes(stationId)) { // 取消选择 const newValues = currentValues.filter((id) => id !== stationId); onValueChange(newValues); } else { // 添加选择 onValueChange([...currentValues, stationId]); } } else { // 单选模式 onValueChange(stationId); setOpen(false); } }; // 清除选择 const handleClear = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); onValueChange?.(multiple ? [] : ''); }; // 处理单个站点移除(仅多选模式) const handleRemoveStation = (stationId: string, e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (multiple) { const currentValues = Array.isArray(value) ? value : value ? [value] : []; const newValues = currentValues.filter((id) => id !== stationId); onValueChange?.(newValues); } }; // 渲染触发器内容 const renderTriggerContent = () => { if (selectedStations.length === 0) { return placeholder; } if (multiple) { if (selectedStations.length === 1) { return selectedStations[0]!.displayName; } else { return ( {selectedStations.slice(0, 1).map((station) => ( {station.displayName} handleRemoveStation(station.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={`移除站点 ${station.displayName}`} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleRemoveStation(station.id, e as any); } }} > ))} {selectedStations.length > 1 && ( +{selectedStations.length - 1} )} ); } } return selectedStations[0]!.displayName; }; // 检查是否选中 const isSelected = (stationId: string) => { if (!value) return false; return Array.isArray(value) ? value.includes(stationId) : value === stationId; }; if (error) { console.error('加载站点列表失败:', error); } return ( {renderTriggerContent()} {allowClear && selectedStations.length > 0 && ( { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClear(e as any); } }} > )} {isLoading ? ( 加载中... ) : filteredStations.length === 0 ? ( 未找到站点 ) : ( {/* 多选模式下显示已选择数量 */} {multiple && selectedStations.length > 0 && ( 已选择 {selectedStations.length} 个站点 )} {filteredStations.map((station) => ( handleSelect(station.id)} /> ))} )} ); } // 导出便捷的单选和多选组件 export function SingleStationSelector( props: Omit & { onValueChange?: (value: string) => void; }, ) { const handleValueChange = (value: string | string[]) => { if (props.onValueChange && typeof value === 'string') { props.onValueChange(value); } }; return ; } export function MultipleStationSelector( props: Omit & { onValueChange?: (value: string[]) => void; }, ) { const handleValueChange = (value: string | string[]) => { if (props.onValueChange && Array.isArray(value)) { props.onValueChange(value); } }; return ; }
{station.description}