diff --git a/web-fe/src/pages/images/components/modalShow/modalShow.tsx b/web-fe/src/pages/images/components/modalShow/modalShow.tsx index d77b773..ca2e8f4 100644 --- a/web-fe/src/pages/images/components/modalShow/modalShow.tsx +++ b/web-fe/src/pages/images/components/modalShow/modalShow.tsx @@ -11,7 +11,6 @@ import { import { getChunkSize, calculateMD5InChunks, - calculateMD5InChunksWorkers, formatTime, } from '@/pages/images/utils/images'; @@ -400,7 +399,9 @@ const ImportModal: React.FC = ({ // 如果有正在上传的文件,调用后端取消上传API if (fileId.current) { try { - await cancelUploadImagesAPI({ file_id: fileId.current }); + const params = new URLSearchParams(); + params.append('file_id', fileId.current); + await cancelUploadImagesAPI(params); } catch (error) { console.error('取消上传API调用失败:', error); } diff --git a/web-fe/src/pages/images/hook/hook.ts b/web-fe/src/pages/images/hook/hook.ts index 0842756..352b84b 100644 --- a/web-fe/src/pages/images/hook/hook.ts +++ b/web-fe/src/pages/images/hook/hook.ts @@ -4,8 +4,9 @@ import { useCallback, useState } from 'react'; const useTableParams = ( initialParams: IMAGES.TableParams = { pagination: { current: 1, pageSize: 10 }, - filters: {}, + filters: {},// 表格的搜索对象 sort: {}, + search: {}, // 添加搜索参数对象 }, ) => { const [tableParams, setTableParams] = @@ -13,8 +14,8 @@ const useTableParams = ( const getApiParams = useCallback(() => { console.log('getApiParams', tableParams); - - const { pagination, filters, sort, ...rest } = tableParams; + + const { pagination, filters, sort, search, ...rest } = tableParams; const apiParams: Record = { page_size: pagination?.pageSize, page_num: pagination?.current, @@ -26,27 +27,52 @@ const useTableParams = ( apiParams.order = sort.order === 'ascend' ? 'asc' : 'desc'; } + // 处理表格搜索参数 Object.entries(filters || {}).forEach(([key, value]) => { if (value !== undefined && value !== null) { apiParams[key] = value; } }); + // 处理搜索参数 + Object.entries(search || {}).forEach(([key, value]) => { + if (value !== undefined && value !== null && value !== '') { + apiParams[key] = value; + } + }); + console.log('getApiParams apiParams', apiParams); return apiParams; }, [tableParams]); - const updateParams = useCallback((newParams: Partial) => { - console.log('updateParams', newParams); - setTableParams((prev) => ({ - ...prev, - ...newParams, - pagination: { - ...prev.pagination, - ...newParams.pagination, - }, - })); - }, []); + // 统一的更新方法,可以处理所有参数类型 + const updateParams = useCallback( + ( + newParams: Partial, + options?: { resetPage?: boolean }, + ) => { + console.log('updateParams', newParams); + + setTableParams((prev) => { + // 如果是搜索或过滤相关的更新,重置到第一页 + const shouldResetPage = + options?.resetPage ?? + ((newParams.search && Object.keys(newParams.search).length > 0) || // 有搜索值 + (newParams.filters && Object.keys(newParams.filters).length > 0)); // 有过滤值 + + return { + ...prev, + ...newParams, + pagination: { + ...prev.pagination, + ...newParams.pagination, + ...(shouldResetPage ? { current: 1 } : {}), // 根据条件决定是否重置页码 + }, + }; + }); + }, + [], + ); /** * 处理表格的分页、排序、过滤等变化 @@ -57,9 +83,11 @@ const useTableParams = ( * @returns void * */ const handleTableChange = useCallback< - NonNullable['onChange']> + NonNullable['onChange']> >( (pagination, filters, sorter, extra) => { + console.log('handleTableChange',pagination,filters,sorter,extra); + // 过滤掉空值的filters const filteredFilters: Record = {}; Object.entries(filters || {}).forEach(([key, value]) => { @@ -104,7 +132,7 @@ const useTableParams = ( return { tableParams, getApiParams, - updateParams, + updateParams, // 统一的更新方法 handleTableChange, }; }; diff --git a/web-fe/src/pages/images/index.tsx b/web-fe/src/pages/images/index.tsx index a3a55f2..2adef88 100644 --- a/web-fe/src/pages/images/index.tsx +++ b/web-fe/src/pages/images/index.tsx @@ -9,6 +9,7 @@ import { Button, Checkbox, Input, + Menu, message, Modal, Popconfirm, @@ -38,6 +39,7 @@ type ColumnConfig = { alwaysVisible?: boolean; // 始终显示的列 filters?: { text: string; value: string }[]; filterMultiple?: boolean; // 是否多选过滤 + filterDropdown?: (props: any) => React.ReactNode; defaultFilteredValue?: string[]; // 默认过滤值 onFilter?: (value: string, record: any) => boolean; }; @@ -52,13 +54,33 @@ type TableColumn = { hidden?: boolean; }; -// 在组件顶部添加防抖函数 -const debounce = (func: Function, delay: number) => { - let timer: NodeJS.Timeout; - return (...args: any[]) => { - clearTimeout(timer); - timer = setTimeout(() => func(...args), delay); +// 在组件顶部添加防抖函数(支持取消) +// 增强版防抖函数 +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 = () => { @@ -78,6 +100,7 @@ const ImageList: React.FC = () => { current: 1, pageSize: 10, }, + search: {}, // 初始化搜索参数 }); // 在组件顶部添加一个 ref 来保存最新的 tableParams @@ -94,7 +117,8 @@ const ImageList: React.FC = () => { tableParams.pagination?.pageSize, tableParams?.sortOrder, tableParams?.sortField, - JSON.stringify(tableParams.filters), + JSON.stringify(tableParams.filters), // 表格搜索参数 + JSON.stringify(tableParams.search), // 搜索参数依赖 ]); // 定义所有列的配置 @@ -144,23 +168,24 @@ const ImageList: React.FC = () => { return {IMAGES_TYPE_MAP[key] || '--'}; }, defaultVisible: true, - filters: [ - { text: '全部', value: '全部' }, - ...Object.entries(IMAGES_TYPE_MAP).map(([key, value]) => ({ - text: value, - value: key, // 保持 key 为字符串 - })), - ], + 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: ['全部'], - // onFilter: (value, record) => { - // // 当选择"全部"时显示所有记录 - // if (value === '全部') { - // return true; - // } - // // 比较时将字符串 value 转换为数字 - // return record.image_type === Number(value); - // }, }, { key: 'storage_path', @@ -263,14 +288,12 @@ const ImageList: React.FC = () => { const apiParams = { ...getApiParams(), }; - if (searchInputRef.current) { - apiParams.image_name = searchInputRef.current; - } + console.log('apiParams', apiParams); + const imagesRes = await getImagesList(apiParams); if (imagesRes.code == CODE) { setImages(imagesRes.data?.data || []); setLoading(false); - console.log('imagesRes', imagesRes); // 正确处理后端返回的分页信息 updateParams({ @@ -278,7 +301,7 @@ const ImageList: React.FC = () => { ...tableParams.pagination, current: imagesRes.data?.page_num || 1, total: imagesRes.data?.total || 0, - pageSize: tableParams.pagination.pageSize|| 10, + pageSize: tableParams.pagination.pageSize || 10, }, }); } else { @@ -305,11 +328,13 @@ const ImageList: React.FC = () => { }; const handleDelete = (record: IMAGES.ImageItem) => { + console.log(record); + Modal.confirm({ title: '确认删除', content: `确定要删除镜像 "${record.image_name}" 吗?`, onOk: () => { - delImagesAPI(record.id).then((res) => { + delImagesAPI({id:record.id}).then((res) => { if (res.code == CODE) { message.success('删除成功'); loadImages(); @@ -399,30 +424,60 @@ const ImageList: React.FC = () => { const handleSearch = useCallback( (searchValue: string) => { - console.log('handleSearch', searchValue, tableParams); + // 只有当搜索值变化时才发送请求 + // if (searchInputRef.current === searchValue) return; + + console.log('实际触发搜索:', searchValue); + // searchInputRef.current = searchValue; - // 使用 ref 中的最新值 const currentTableParams = tableParamsRef.current; - - // 只有当搜索值变化时才更新参数和触发请求 - if (searchInputRef.current !== searchValue) { - searchInputRef.current = searchValue; - updateParams({ - pagination: { - current: 1, - pageSize: currentTableParams.pagination?.pageSize || 10, - }, - filters: { - ...currentTableParams.filters, - image_name: searchValue ? searchValue : undefined, - }, - }); - } + 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(); + console.log('输入变化:', value); + + // 清空时立即触发搜索 + if (value === '') { + immediateSearch(''); + return; + } + + // 正常输入时使用防抖 + debouncedSearch(value); + }; + + // 修改回车搜索处理 + const handleEnterSearch = (value: string) => { + console.log('回车搜索:', value); + + // 回车搜索时取消未执行的防抖 + debouncedSearch.cancel(); + immediateSearch.cancel(); + + // 直接执行搜索 + handleSearch(value); + }; return (
@@ -432,18 +487,9 @@ const ImageList: React.FC = () => { { - const value = e.target.value; - setSearchText(value); - // 只有当输入为空时立即触发搜索,否则使用防抖 - if (value === '') { - handleSearch(''); - } else { - debouncedSearch(value); - } - }} + onChange={(e) => handleSearchChange(e.target.value)} style={{ width: 300 }} - onSearch={handleSearch} + onSearch={handleEnterSearch} />