import React from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Button } from '@/client/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/client/components/ui/table'; import { DataTablePagination } from '../components/DataTablePagination'; import { Plus, Edit, Trash2, Search, Power, ListTree, Table as TableIcon, RotateCcw } from 'lucide-react'; import { useState, useCallback } from 'react'; import { areaClient } from '@/client/api'; import type { InferResponseType, InferRequestType } from 'hono/client'; import { Input } from '@/client/components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select'; import { Badge } from '@/client/components/ui/badge'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/client/components/ui/dialog'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/client/components/ui/alert-dialog'; import { AreaForm } from '../components/AreaForm'; import { AreaTree } from '../components/AreaTree'; import type { CreateAreaInput, UpdateAreaInput } from '@d8d/server/modules/areas/area.schema'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/client/components/ui/tabs'; // 类型提取规范 type AreaResponse = InferResponseType['data'][0]; type SearchAreaRequest = InferRequestType['query']; type CreateAreaRequest = InferRequestType['json']; type UpdateAreaRequest = InferRequestType['json']; // 树形节点类型 interface AreaNode { id: number; name: string; code: string; level: number; parentId: number | null; isDisabled: number; children?: AreaNode[]; } // 统一操作处理函数 const handleOperation = async (operation: () => Promise) => { try { await operation(); // toast.success('操作成功'); console.log('操作成功'); } catch (error) { console.error('操作失败:', error); // toast.error('操作失败,请重试'); throw error; } }; // 防抖搜索函数 const debounce = (func: Function, delay: number) => { let timeoutId: NodeJS.Timeout; return (...args: any[]) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; }; export const AreasPage: React.FC = () => { const queryClient = useQueryClient(); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(20); const [keyword, setKeyword] = useState(''); const [level, setLevel] = useState('all'); const [parentId, setParentId] = useState(''); const [isDisabled, setIsDisabled] = useState('all'); const [viewMode, setViewMode] = useState<'table' | 'tree'>('table'); const [expandedNodes, setExpandedNodes] = useState>(new Set()); const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [isStatusDialogOpen, setIsStatusDialogOpen] = useState(false); const [selectedArea, setSelectedArea] = useState(null); // 构建搜索参数 const filters: Record = {}; if (level && level !== 'all') filters.level = Number(level); if (parentId) filters.parentId = Number(parentId); if (isDisabled && isDisabled !== 'all') filters.isDisabled = Number(isDisabled); const searchParams:SearchAreaRequest = { page, pageSize, keyword: keyword || undefined, filters: Object.keys(filters).length > 0 ? JSON.stringify(filters) : undefined, sortBy: 'id', sortOrder: 'ASC' }; // 查询省市区列表 const { data, isLoading } = useQuery({ queryKey: ['areas', searchParams], queryFn: async () => { const res = await areaClient.$get({ query: searchParams }); if (res.status !== 200) throw new Error('获取省市区列表失败'); return await res.json(); }, staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, }); // 查询树形结构数据 const { data: treeData, isLoading: isTreeLoading } = useQuery({ queryKey: ['areas-tree'], queryFn: async () => { const res = await areaClient.tree.$get(); if (res.status !== 200) throw new Error('获取省市区树形数据失败'); const response = await res.json(); return response.data; }, staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, enabled: viewMode === 'tree' }); // 创建省市区 const createMutation = useMutation({ mutationFn: async (data: CreateAreaRequest) => { await handleOperation(async () => { const res = await areaClient.$post({ json: data }); if (res.status !== 201) throw new Error('创建省市区失败'); }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['areas'] }); setIsCreateDialogOpen(false); }, }); // 更新省市区 const updateMutation = useMutation({ mutationFn: async ({ id, data }: { id: number; data: UpdateAreaRequest }) => { await handleOperation(async () => { const res = await areaClient[':id'].$put({ param: { id }, json: data }); if (res.status !== 200) throw new Error('更新省市区失败'); }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['areas'] }); setIsEditDialogOpen(false); setSelectedArea(null); }, }); // 删除省市区 const deleteMutation = useMutation({ mutationFn: async (id: number) => { await handleOperation(async () => { const res = await areaClient[':id'].$delete({ param: { id } }); if (res.status !== 204) throw new Error('删除省市区失败'); }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['areas'] }); setIsDeleteDialogOpen(false); setSelectedArea(null); }, }); // 启用/禁用省市区 const toggleStatusMutation = useMutation({ mutationFn: async ({ id, isDisabled }: { id: number; isDisabled: number }) => { await handleOperation(async () => { const res = await areaClient[':id'].$put({ param: { id }, json: { isDisabled } }); if (res.status !== 200) throw new Error('更新省市区状态失败'); }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['areas'] }); setIsStatusDialogOpen(false); setSelectedArea(null); }, }); // 防抖搜索 const debouncedSearch = useCallback( debounce((keyword: string) => { setKeyword(keyword); setPage(1); }, 300), [] ); // 处理筛选变化 const handleFilterChange = (filterType: string, value: string) => { switch (filterType) { case 'level': setLevel(value); break; case 'parentId': setParentId(value); break; case 'isDisabled': setIsDisabled(value); break; } setPage(1); }; // 处理创建省市区 const handleCreateArea = async (data: CreateAreaInput | UpdateAreaInput) => { await createMutation.mutateAsync(data as CreateAreaInput); }; // 处理更新省市区 const handleUpdateArea = async (data: UpdateAreaInput) => { if (!selectedArea) return; await updateMutation.mutateAsync({ id: selectedArea.id, data }); }; // 处理删除省市区 const handleDeleteArea = async () => { if (!selectedArea) return; await deleteMutation.mutateAsync(selectedArea.id); }; // 处理启用/禁用省市区 const handleToggleStatus = async (isDisabled: number) => { if (!selectedArea) return; await toggleStatusMutation.mutateAsync({ id: selectedArea.id, isDisabled }); }; // 打开编辑对话框 const handleEdit = (area: AreaNode) => { // 将 AreaNode 转换为 AreaResponse const areaResponse: AreaResponse = { ...area, isDeleted: 0, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), createdBy: null, updatedBy: null }; setSelectedArea(areaResponse); setIsEditDialogOpen(true); }; // 打开删除对话框 const handleDelete = (area: AreaNode) => { // 将 AreaNode 转换为 AreaResponse const areaResponse: AreaResponse = { ...area, isDeleted: 0, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), createdBy: null, updatedBy: null }; setSelectedArea(areaResponse); setIsDeleteDialogOpen(true); }; // 打开状态切换对话框 const handleToggleStatusDialog = (area: AreaNode) => { // 将 AreaNode 转换为 AreaResponse const areaResponse: AreaResponse = { ...area, isDeleted: 0, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), createdBy: null, updatedBy: null }; setSelectedArea(areaResponse); setIsStatusDialogOpen(true); }; // 切换节点展开状态 const handleToggleNode = (nodeId: number) => { setExpandedNodes(prev => { const newSet = new Set(prev); if (newSet.has(nodeId)) { newSet.delete(nodeId); } else { newSet.add(nodeId); } return newSet; }); }; // 重置筛选 const handleResetFilters = () => { setKeyword(''); setLevel('all'); setParentId(''); setIsDisabled('all'); setPage(1); }; // 获取层级显示名称 const getLevelName = (level: number) => { switch (level) { case 1: return '省/直辖市'; case 2: return '市'; case 3: return '区/县'; default: return '未知'; } }; return (

省市区管理

管理省市区三级联动数据

setViewMode(value as 'table' | 'tree')}> 表格视图 树形视图 {/* 表格视图 */} 省市区列表 查看和管理所有省市区数据 {/* 搜索和筛选区域 */}
{ setKeyword(e.target.value); debouncedSearch(e.target.value); }} />
{/* 数据表格 */}
ID 名称 代码 层级 父级ID 状态 创建时间 操作 {isLoading ? ( 加载中... ) : !data?.data || data.data.length === 0 ? ( 暂无数据 ) : ( data.data.map((area) => ( {area.id} {area.name} {area.code} {getLevelName(area.level)} {area.parentId || '-'} {area.isDisabled === 0 ? '启用' : '禁用'} {new Date(area.createdAt).toLocaleDateString('zh-CN')}
)) )}
{/* 分页 */} {data && (
{ setPage(newPage); setPageSize(newPageSize); }} />
)}
{/* 树形视图 */} 省市区树形结构 以树形结构查看和管理省市区层级关系 {isTreeLoading ? (
加载中...
) : !treeData || treeData.length === 0 ? (
暂无数据
) : ( )}
{/* 创建省市区对话框 */} 新增省市区 填写省市区信息 setIsCreateDialogOpen(false)} /> {/* 编辑省市区对话框 */} 编辑省市区 修改省市区信息 {selectedArea && ( { setIsEditDialogOpen(false); setSelectedArea(null); }} /> )} {/* 删除确认对话框 */} 确认删除 确定要删除省市区 "{selectedArea?.name}" 吗?此操作不可恢复。 取消 {deleteMutation.isPending ? '删除中...' : '确认删除'} {/* 状态切换确认对话框 */} {selectedArea?.isDisabled === 0 ? '禁用' : '启用'}确认 确定要{selectedArea?.isDisabled === 0 ? '禁用' : '启用'}省市区 "{selectedArea?.name}" 吗? 取消 handleToggleStatus(selectedArea?.isDisabled === 0 ? 1 : 0)} disabled={toggleStatusMutation.isPending} > {toggleStatusMutation.isPending ? '处理中...' : '确认'}
); };