diff --git a/frontend/src/pages/Deploy/Team/List/index.tsx b/frontend/src/pages/Deploy/Team/List/index.tsx index 491aa877..a3da87bf 100644 --- a/frontend/src/pages/Deploy/Team/List/index.tsx +++ b/frontend/src/pages/Deploy/Team/List/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect, useMemo, useRef } from 'react'; import { PageContainer } from '@/components/ui/page-container'; import { getTeams } from './service'; import type { TeamResponse, TeamQuery } from './types'; @@ -10,44 +10,17 @@ import type { UserResponse } from '@/pages/System/User/List/types'; import { getEnvironmentList } from './service'; import type { Environment } from './types'; import { TeamEnvironmentManageDialog } from './components/TeamEnvironmentManageDialog'; -import { - Table, - TableHeader, - TableBody, - TableHead, - TableRow, - TableCell, -} from '@/components/ui/table'; -import { - Card, - CardContent, - CardHeader, - CardTitle, - CardDescription, -} from '@/components/ui/card'; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; +import { Badge } from '@/components/ui/badge'; +import { PaginatedTable, type ColumnDef, type SearchFieldDef, type PaginatedTableRef } from '@/components/ui/paginated-table'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; -import { Badge } from '@/components/ui/badge'; -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { searchFormSchema, type SearchFormValues } from './schema'; -import { DataTablePagination } from '@/components/ui/pagination'; -import { DEFAULT_PAGE_SIZE, DEFAULT_PAGE_NUM } from '@/utils/page'; -import type { Page } from '@/types/base'; import { Plus, Edit, @@ -60,23 +33,8 @@ import { Settings, } from 'lucide-react'; - -type Column = { - accessorKey?: keyof TeamResponse; - id?: string; - header: string; - size?: number; - cell?: (props: { row: { original: TeamResponse } }) => React.ReactNode; -}; - const TeamList: React.FC = () => { - const [list, setList] = useState([]); - const [pagination, setPagination] = useState({ - pageNum: DEFAULT_PAGE_NUM, - pageSize: DEFAULT_PAGE_SIZE, - totalElements: 0, - }); - + const tableRef = useRef>(null); const [modalVisible, setModalVisible] = useState(false); const [currentTeam, setCurrentTeam] = useState(); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); @@ -84,48 +42,7 @@ const TeamList: React.FC = () => { const [envManageDialogOpen, setEnvManageDialogOpen] = useState(false); const [users, setUsers] = useState([]); const [environments, setEnvironments] = useState([]); - - const form = useForm({ - resolver: zodResolver(searchFormSchema), - defaultValues: { - teamCode: '', - teamName: '', - enabled: undefined, - }, - }); - - // 统计数据 - const stats = useMemo(() => { - const total = pagination.totalElements; - const enabled = list.filter((item) => item.enabled).length; - const disabled = list.filter((item) => !item.enabled).length; - return { total, enabled, disabled }; - }, [list, pagination.totalElements]); - - // 加载数据 - const loadData = async (searchValues?: SearchFormValues) => { - try { - const query: TeamQuery = { - pageNum: pagination.pageNum, - pageSize: pagination.pageSize, - teamCode: searchValues?.teamCode || form.getValues('teamCode') || undefined, - teamName: searchValues?.teamName || form.getValues('teamName') || undefined, - enabled: searchValues?.enabled !== undefined ? searchValues.enabled : form.getValues('enabled'), - }; - - const response: Page | undefined = await getTeams(query); - - if (response) { - setList(response.content || []); - setPagination({ - ...pagination, - totalElements: response.totalElements || 0, - }); - } - } catch (error) { - console.error('加载数据失败:', error); - } - }; + const [stats, setStats] = useState({ total: 0, enabled: 0, disabled: 0 }); // 加载基础数据(仅一次) useEffect(() => { @@ -155,13 +72,21 @@ const TeamList: React.FC = () => { } }; - - useEffect(() => { - loadData(); - }, [pagination.pageNum, pagination.pageSize]); + // 包装 fetchFn,同时更新统计数据 + const fetchData = async (query: TeamQuery) => { + const result = await getTeams(query); + // 更新统计 + const all = result.content || []; + setStats({ + total: result.totalElements || 0, + enabled: all.filter((item) => item.enabled).length, + disabled: all.filter((item) => !item.enabled).length, + }); + return result; + }; const handleSuccess = () => { - loadData(); + tableRef.current?.refresh(); }; const handleAdd = () => { @@ -196,91 +121,85 @@ const TeamList: React.FC = () => { } }; - const handlePageChange = (newPage: number) => { - setPagination({ ...pagination, pageNum: newPage + 1 }); - }; + // 搜索字段定义 + const searchFields: SearchFieldDef[] = useMemo(() => [ + { key: 'teamCode', type: 'input', placeholder: '团队编码', width: 'w-[180px]' }, + { key: 'teamName', type: 'input', placeholder: '团队名称', width: 'w-[180px]' }, + { + key: 'enabled', + type: 'select', + placeholder: '状态', + width: 'w-[120px]', + options: [ + { label: '启用', value: 'true' }, + { label: '禁用', value: 'false' }, + ], + }, + ], []); - const columns: Column[] = [ + // 列定义 + const columns: ColumnDef[] = useMemo(() => [ + { key: 'id', title: 'ID', dataIndex: 'id', width: '80px' }, + { key: 'teamCode', title: '团队编码', dataIndex: 'teamCode', width: '180px' }, + { key: 'teamName', title: '团队名称', dataIndex: 'teamName', width: '180px' }, + { key: 'description', title: '团队描述', dataIndex: 'description', width: '260px' }, { - accessorKey: 'id', - header: 'ID', - size: 80, + key: 'ownerName', + title: '负责人', + width: '140px', + render: (_, record) => record.ownerName || '-', }, { - accessorKey: 'teamCode', - header: '团队编码', - size: 180, - }, - { - accessorKey: 'teamName', - header: '团队名称', - size: 180, - }, - { - accessorKey: 'description', - header: '团队描述', - size: 260, - }, - { - accessorKey: 'ownerName', - header: '负责人', - size: 140, - cell: ({ row }) => row.original.ownerName || '-', - }, - { - id: 'memberCount', - header: '成员数量', - size: 100, - cell: ({ row }) => ( + key: 'memberCount', + title: '成员数量', + width: '100px', + render: (_, record) => (
- {row.original.memberCount || 0} + {record.memberCount || 0}
), }, { - id: 'environmentCount', - header: '环境数量', - size: 100, - cell: ({ row }) => ( + key: 'environmentCount', + title: '环境数量', + width: '100px', + render: (_, record) => (
- {row.original.environmentCount || 0} + {record.environmentCount || 0}
), }, { - id: 'applicationCount', - header: '应用数量', - size: 100, - cell: ({ row }) => ( + key: 'applicationCount', + title: '应用数量', + width: '100px', + render: (_, record) => (
- {row.original.applicationCount || 0} + {record.applicationCount || 0}
), }, { - accessorKey: 'enabled', - header: '状态', - size: 100, - cell: ({ row }) => ( - - {row.original.enabled ? '启用' : '禁用'} + key: 'enabled', + title: '状态', + width: '100px', + render: (_, record) => ( + + {record.enabled ? '启用' : '禁用'} ), }, + { key: 'sort', title: '排序', dataIndex: 'sort', width: '100px' }, { - accessorKey: 'sort', - header: '排序', - size: 100, - }, - { - id: 'actions', - header: '操作', - size: 180, - cell: ({ row }) => ( + key: 'actions', + title: '操作', + width: '120px', + sticky: true, + render: (_, record) => (
@@ -290,20 +209,20 @@ const TeamList: React.FC = () => { - handleManageEnvironments(row.original)}> + handleManageEnvironments(record)}> 环境管理 - handleManageMembers(row.original)}> + handleManageMembers(record)}> 管理成员 - handleEdit(row.original)}> + handleEdit(record)}> 编辑团队 handleDelete(row.original)} + onClick={() => handleDelete(record)} className="text-destructive focus:text-destructive" > @@ -314,7 +233,15 @@ const TeamList: React.FC = () => {
), }, - ]; + ], []); + + // 工具栏 + const toolbar = ( + + ); return ( @@ -355,115 +282,24 @@ const TeamList: React.FC = () => { {/* 团队管理 */} -
-
- 团队管理 - - 创建和管理团队,分配团队成员和应用 - -
-
- -
+
+ 团队管理 + + 创建和管理团队,分配团队成员和应用 +
- {/* 搜索过滤 */} -
- form.setValue('teamCode', e.target.value)} - className="max-w-[200px]" - /> - form.setValue('teamName', e.target.value)} - className="max-w-[200px]" - /> - - - -
- - {/* 数据表格 */} -
- - - - {columns.map((column) => ( - - {column.header} - - ))} - - - - {list.length === 0 ? ( - - - 暂无数据 - - - ) : ( - list.map((item) => ( - - {columns.map((column) => { - let cellContent; - if (column.cell) { - cellContent = column.cell({ row: { original: item } }); - } else if (column.accessorKey) { - const value = item[column.accessorKey]; - cellContent = value != null ? String(value) : ''; - } else { - cellContent = ''; - } - return ( - - {cellContent} - - ); - })} - - )) - )} - -
-
- -
-
+ + ref={tableRef} + fetchFn={fetchData} + columns={columns} + searchFields={searchFields} + toolbar={toolbar} + rowKey="id" + minWidth="1440px" + />
@@ -507,4 +343,3 @@ const TeamList: React.FC = () => { }; export default TeamList; -