| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- import React from 'react';
- import { ChevronRight, ChevronDown, Folder, FolderOpen } from 'lucide-react';
- import { Button } from '@/client/components/ui/button';
- import { Badge } from '@/client/components/ui/badge';
- import { cn } from '@/client/lib/utils';
- interface AreaNode {
- id: number;
- name: string;
- code: string;
- level: number;
- parentId: number | null;
- isDisabled: number;
- children?: AreaNode[];
- }
- interface AreaTreeProps {
- areas: AreaNode[];
- expandedNodes: Set<number>;
- onToggleNode: (nodeId: number) => void;
- onEdit: (area: AreaNode) => void;
- onDelete: (area: AreaNode) => void;
- onToggleStatus: (area: AreaNode) => void;
- }
- export const AreaTree: React.FC<AreaTreeProps> = ({
- areas,
- expandedNodes,
- onToggleNode,
- onEdit,
- onDelete,
- onToggleStatus
- }) => {
- const renderTreeNode = (node: AreaNode, depth = 0) => {
- const hasChildren = node.children && node.children.length > 0;
- const isExpanded = expandedNodes.has(node.id);
- const isDisabled = node.isDisabled === 1;
- return (
- <div key={node.id} className="select-none">
- {/* 节点行 */}
- <div
- className={cn(
- "flex items-center gap-2 py-2 px-3 hover:bg-muted/50 cursor-pointer border-b",
- depth > 0 && "ml-6"
- )}
- style={{ marginLeft: `${depth * 24}px` }}
- >
- {/* 展开/收起按钮 */}
- {hasChildren && (
- <Button
- variant="ghost"
- size="sm"
- className="h-6 w-6 p-0"
- onClick={() => onToggleNode(node.id)}
- >
- {isExpanded ? (
- <ChevronDown className="h-4 w-4" />
- ) : (
- <ChevronRight className="h-4 w-4" />
- )}
- </Button>
- )}
- {!hasChildren && <div className="w-6" />}
- {/* 图标 */}
- <div className="flex-shrink-0">
- {hasChildren ? (
- isExpanded ? (
- <FolderOpen className="h-4 w-4 text-blue-500" />
- ) : (
- <Folder className="h-4 w-4 text-blue-400" />
- )
- ) : (
- <div className="h-4 w-4" />
- )}
- </div>
- {/* 节点信息 */}
- <div className="flex-1 flex items-center gap-3">
- <span className={cn("font-medium", isDisabled && "text-muted-foreground line-through")}>
- {node.name}
- </span>
- <Badge variant="outline" className="text-xs">
- {getLevelName(node.level)}
- </Badge>
- <span className="text-xs text-muted-foreground">
- {node.code}
- </span>
- <Badge variant={isDisabled ? "secondary" : "default"} className="text-xs">
- {isDisabled ? '禁用' : '启用'}
- </Badge>
- </div>
- {/* 操作按钮 */}
- <div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
- <Button
- variant="ghost"
- size="sm"
- onClick={(e) => {
- e.stopPropagation();
- onEdit(node);
- }}
- >
- 编辑
- </Button>
- <Button
- variant="ghost"
- size="sm"
- onClick={(e) => {
- e.stopPropagation();
- onToggleStatus(node);
- }}
- >
- {isDisabled ? '启用' : '禁用'}
- </Button>
- <Button
- variant="ghost"
- size="sm"
- onClick={(e) => {
- e.stopPropagation();
- onDelete(node);
- }}
- >
- 删除
- </Button>
- </div>
- </div>
- {/* 子节点 */}
- {isExpanded && hasChildren && (
- <div>
- {node.children!.map(child => renderTreeNode(child, depth + 1))}
- </div>
- )}
- </div>
- );
- };
- return (
- <div className="border rounded-lg bg-background">
- {areas.map(area => renderTreeNode(area))}
- </div>
- );
- };
- // 获取层级显示名称
- const getLevelName = (level: number) => {
- switch (level) {
- case 1: return '省/直辖市';
- case 2: return '市';
- case 3: return '区/县';
- default: return '未知';
- }
- };
|