/* eslint-disable @typescript-eslint/no-use-before-define */ import { CODE } from '@/constants/images.constants'; import { deleteTool, getToollList } from '@/services/imagePage'; import { SettingOutlined,PlusOutlined } from '@ant-design/icons'; import { Button, Checkbox, Input, message, Modal, Popconfirm, Popover, Space, Table, Tooltip, } from 'antd'; import dayjs from 'dayjs'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import useTableParams from './hook/hook'; import './index.less'; import UploadFileModal from './uploadFileModal'; import { ReactComponent as RefreshIcon } from '@/assets/icons/refresh.svg'; // 列配置定义 type ColumnConfig = { key: string; title: string; dataIndex?: string; width: number; render?: (text: any, record: any, index: number) => React.ReactNode; fixed?: 'left' | 'right'; align?: 'left' | 'center' | 'right'; defaultVisible: boolean; // 默认是否显示 alwaysVisible?: boolean; // 始终显示的列 ellipsis?: boolean; // 是否启用省略号 filters?: { text: string; value: string }[]; filterMultiple?: boolean; // 是否多选过滤 filterDropdown?: (props: any) => React.ReactNode; defaultFilteredValue?: string[]; // 默认过滤值 onFilter?: (value: string, record: any) => boolean; }; type TableColumn = { title: string; dataIndex?: string; key: string; width: number; render?: any; fixed?: 'left' | 'right'; hidden?: boolean; }; // 在组件顶部添加防抖函数(支持取消) // 增强版防抖函数,使用泛型明确函数类型 const debounce = any>( func: T, delay: number, immediate = false, ) => { let timer: NodeJS.Timeout | null = null; const debounced = (...args: Parameters) => { if (timer) clearTimeout(timer); if (immediate && !timer) { func(...args); } timer = setTimeout(() => { if (!immediate) { func(...args); } timer = null; }, delay); }; debounced.cancel = () => { if (timer) { clearTimeout(timer); timer = null; } }; return debounced; }; const Index: React.FC = (props) => { const { activeTabKey } = props; const [dataSource, setDataSource] = useState([]); const [loading, setLoading] = useState(false); const [selectedImage, setSelectedImage] = useState(null); const [importModalVisible, setImportModalVisible] = useState(false); const [searchText, setSearchText] = useState(''); // 添加本地搜索状态 const { tableParams, getApiParams, updateParams, handleTableChange } = useTableParams({ pagination: { current: 1, pageSize: 10, }, search: {}, // 初始化搜索参数 }); // 在组件顶部添加一个 ref 来保存最新的 tableParams const tableParamsRef = useRef(tableParams); tableParamsRef.current = tableParams; // 每次渲染时更新 ref 的值 const [columnSettingsVisible, setColumnSettingsVisible] = useState(false); // 表格参数变化 获取镜像列表 useEffect(() => { if (activeTabKey === '4') { loadDataSource(); } }, [ activeTabKey, tableParams.pagination?.current, tableParams.pagination?.pageSize, tableParams?.sortOrder, tableParams?.sortField, JSON.stringify(tableParams.filters), // 表格搜索参数 JSON.stringify(tableParams.search), // 搜索参数依赖 ]); // 定义所有列的配置 const columnConfigs: ColumnConfig[] = [ { key: 'index', title: '序号', width: 60, render: (text: any, row: any, index: number) => (tableParams.pagination?.current - 1) * tableParams.pagination?.pageSize + index + 1, defaultVisible: true, alwaysVisible: true, }, { key: 'tool_name', // title: '镜像名称', title: '文件名', dataIndex: 'tool_name', width: 150, defaultVisible: true, alwaysVisible: true, ellipsis: true, render: (text: string) => text ? ( {text} ) : ( '--' ), }, { key: 'tool_type', title: '文件类型', dataIndex: 'tool_type', width: 100, defaultVisible: true, ellipsis: true, render: (text: string) => text ? {text} : '--', }, { key: 'file_size', // title: '镜像版本', title: '文件大小', dataIndex: 'file_size', width: 100, defaultVisible: true, ellipsis: true, render: (text: string) => text ? {text} : '--', }, { key: 'version', // title: '模板存放路径', title: '版本', dataIndex: 'version', width: 140, defaultVisible: true, ellipsis: true, render: (text: string) => text ? ( {text} ) : ( '--' ), }, { key: 'create_time', title: '上传时间', dataIndex: 'create_time', width: 160, render: (text: string) => text ? ( {text ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '--'} ) : ( '--' ), defaultVisible: true, ellipsis: true, }, { key: 'description', title: '描述', dataIndex: 'description', width: 140, defaultVisible: true, ellipsis: true, render: (text: string) => text ? ( {text} ) : ( '--' ), }, { key: 'action', title: '操作', width: 90, align: 'center', fixed: 'right' as 'right', render: (_: any, record: TOOL.ToolItem) => ( handleDelete(record)} okText="确定" cancelText="取消" > ), defaultVisible: true, }, ]; // 初始化 visibleColumns 状态 const initialVisibleColumns = columnConfigs.reduce>( (acc, column) => { if (!column.alwaysVisible) { acc[column.key] = column.defaultVisible; } return acc; }, {}, ); const [visibleColumns, setVisibleColumns] = useState>( initialVisibleColumns, ); // 重置列设置 const resetColumns = () => { setVisibleColumns(initialVisibleColumns); }; const loadDataSource = async () => { setLoading(true); try { // 将搜索文本合并到API参数中 const apiParams = { ...getApiParams(), }; const imagesRes = await getToollList(apiParams); if (imagesRes.code === CODE) { setDataSource(imagesRes.data?.data || []); setLoading(false); // 正确处理后端返回的分页信息 updateParams({ pagination: { ...tableParams.pagination, current: imagesRes.data?.page_num || 1, total: imagesRes.data?.total || 0, pageSize: tableParams.pagination?.pageSize || 10, }, }); } else { message.error(imagesRes.message || '获取镜像列表失败'); setLoading(false); } } catch (err) { message.error('获取镜像列表失败'); setLoading(false); } }; const handleViewDetail = (record: TOOL.ToolItem) => { setSelectedImage(record); setImportModalVisible(true); }; const handleDelete = (record: TOOL.ToolItem) => { Modal.confirm({ title: '确认删除', content: `确定要删除镜像 "${record.image_name}" 吗?`, onOk: () => { deleteTool({ id: record.id }).then((res) => { if (res.code === CODE) { message.success('删除成功'); loadDataSource(); } else { message.error(res.message || '删除失败'); } }); }, }); }; // 列设置相关函数 const handleColumnChange = (columnKey: string, checked: boolean) => { setVisibleColumns((prev) => ({ ...prev, [columnKey]: checked, })); }; // 列设置内容 const columnSettingsContent = (
{columnConfigs .filter((config) => !config.alwaysVisible) // 只显示可控制的列 .map((config) => (
handleColumnChange(config.key, e.target.checked)} > {config.title}
))}
); // 根据visibleColumns过滤显示的列 const filteredColumns = columnConfigs .map((config) => { // 对于始终显示的列 if (config.alwaysVisible) { return { ...config, hidden: undefined, }; } // 对于可控制显示/隐藏的列 return { ...config, ...(visibleColumns[config.key] ? {} : { hidden: true }), }; }) .filter((column) => !column.hidden) as TableColumn[]; const handleRefresh = () => { loadDataSource(); }; // 自定义分页配置 const paginationConfig = { ...tableParams.pagination, showTotal: (total: number) => `共 ${total} 条记录`, showSizeChanger: true, showQuickJumper: true, pageSizeOptions: ['10', '20', '50', '100'], }; const handleSearch = useCallback( (searchValue: string) => { const currentTableParams = tableParamsRef.current; updateParams({ search: { image_name: searchValue, }, pagination: { current: 1, pageSize: currentTableParams.pagination?.pageSize || 10, }, }); }, [updateParams], ); // 防抖版本(500ms延迟,不立即执行) const debouncedSearch = useRef(debounce(handleSearch, 500)).current; // 立即执行版本(用于清空时立即搜索) const immediateSearch = useRef(debounce(handleSearch, 0, true)).current; const handleSearchChange = (value: string) => { setSearchText(value); // 取消所有未执行的防抖请求 debouncedSearch.cancel(); immediateSearch.cancel(); // 清空时立即触发搜索 if (value === '') { immediateSearch(''); return; } // 正常输入时使用防抖 debouncedSearch(value); }; // 修改回车搜索处理 const handleEnterSearch = (value: string) => { // 回车搜索时取消未执行的防抖 debouncedSearch.cancel(); immediateSearch.cancel(); // 直接执行搜索 handleSearch(value); }; const onSubmitBack = () => { setImportModalVisible(false); setSelectedImage(null); handleRefresh(); }; return (
handleSearchChange(e.target.value)} style={{ width: 300 }} onSearch={handleEnterSearch} />
{importModalVisible && ( { setImportModalVisible(false); setSelectedImage(null); }} onSubmit={onSubmitBack} isEditing={selectedImage ? true : false} initialValues={selectedImage} /> )} ); }; export default Index;