import { CODE, IMAGES_TYPE_MAP } from '@/constants/images.constants'; import { delImagesAPI, getImagesList } from '@/services/images'; import { DeleteOutlined, EyeOutlined, SettingOutlined, } from '@ant-design/icons'; import { Button, Checkbox, Input, Menu, message, Modal, Popconfirm, Popover, Space, Table, Tag, Tooltip, } from 'antd'; import dayjs from 'dayjs'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { ImportModal, ModalDetailShow } from './components/modalShow/modalShow'; import useTableParams from './hook/hook'; import './index.less'; 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'; 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 = (func: Function, delay: number, immediate = false) => { let timer: NodeJS.Timeout | null = null; const debounced = (...args: any[]) => { 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 ImageList: React.FC = () => { const [images, setImages] = useState([]); const [loading, setLoading] = useState(false); const [selectedImage, setSelectedImage] = useState( null, ); const [detailVisible, setDetailVisible] = useState(false); const [importModalVisible, setImportModalVisible] = useState(false); const [searchText, setSearchText] = useState(''); // 添加本地搜索状态 const searchInputRef = useRef(''); // 保存已发送请求的搜索文本 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(() => { loadImages(); }, [ 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: 'image_name', title: '镜像名称', dataIndex: 'image_name', width: 200, defaultVisible: true, alwaysVisible: true, ellipsis: true, render: (text: string) => text ? ( {text} ) : ( '--' ), }, { key: 'image_type', title: '桌面类型', dataIndex: 'image_type', width: 120, render: (text: number) => { const key = text as keyof typeof IMAGES_TYPE_MAP; return text ? IMAGES_TYPE_MAP[key] : '--'; }, defaultVisible: true, filterDropdown: ({ setSelectedKeys, selectedKeys, confirm }) => ( 0 ? selectedKeys : ['全部']} onClick={({ key }) => { setSelectedKeys(key === '全部' ? [] : [key]); confirm({ closeDropdown: true }); // 立即触发筛选并关闭下拉菜单 }} items={[ { key: '全部', label: '全部' }, ...Object.entries(IMAGES_TYPE_MAP).map(([key, value]) => ({ key, label: value, })), ]} /> ), filterMultiple: false, defaultFilteredValue: ['全部'], }, { key: 'storage_path', title: '模板存放路径', dataIndex: 'storage_path', width: 140, defaultVisible: true, ellipsis: true, render: (text: string) => text ? {text}: '--' }, { key: 'bt_path', title: 'BT路径', dataIndex: 'bt_path', width: 250, defaultVisible: true, ellipsis: true, render: (text: string) => text ? {text}:'--' }, { key: 'image_version', title: '镜像版本', dataIndex: 'image_version', width: 100, defaultVisible: true, ellipsis: true, render: (text: string) => text ? {text} : '--', }, { key: 'os_version', title: '操作系统', dataIndex: 'os_version', width: 100, defaultVisible: true, ellipsis: true, render: (text: string) => text ? {text} : '--', }, { key: 'image_status', title: '镜像状态', dataIndex: 'image_status', width: 80, render: (text: number) => (text ? getStatusTag(text) : '--'), defaultVisible: true, }, { key: 'create_time', title: '创建时间', dataIndex: 'create_time', width: 180, render: (text: string) => text ? ( {text ? dayjs(text).format('YYYY-MM-DD HH:mm:ss') : '--'} ) : ( '--' ), defaultVisible: true, ellipsis: true, }, { key: 'action', title: '操作', width: 100, fixed: 'right' as 'right', render: (_: any, record: IMAGES.ImageItem) => ( ); // 根据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 = () => { loadImages(); }; // 导入镜像成功后的回调 const handleImportSuccess = () => { setTimeout(() => { loadImages(); }, 5000); }; // 自定义分页配置 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); }; return (
handleSearchChange(e.target.value)} style={{ width: 300 }} onSearch={handleEnterSearch} />
{detailVisible ? ( ) : null} {/* 导入弹窗 */} setImportModalVisible(false)} onImportSuccess={handleImportSuccess} /> ); }; export default ImageList;