fix(镜像列表): 取消上传+参数逻辑
parent
0cae7e90bd
commit
a1dbc6a736
|
@ -11,7 +11,6 @@ import {
|
|||
import {
|
||||
getChunkSize,
|
||||
calculateMD5InChunks,
|
||||
calculateMD5InChunksWorkers,
|
||||
formatTime,
|
||||
} from '@/pages/images/utils/images';
|
||||
|
||||
|
@ -400,7 +399,9 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
|||
// 如果有正在上传的文件,调用后端取消上传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);
|
||||
}
|
||||
|
|
|
@ -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] =
|
||||
|
@ -14,7 +15,7 @@ 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<string, any> = {
|
||||
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<IMAGES.TableParams>) => {
|
||||
// 统一的更新方法,可以处理所有参数类型
|
||||
const updateParams = useCallback(
|
||||
(
|
||||
newParams: Partial<IMAGES.TableParams>,
|
||||
options?: { resetPage?: boolean },
|
||||
) => {
|
||||
console.log('updateParams', newParams);
|
||||
setTableParams((prev) => ({
|
||||
|
||||
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 } : {}), // 根据条件决定是否重置页码
|
||||
},
|
||||
}));
|
||||
}, []);
|
||||
};
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
/**
|
||||
* 处理表格的分页、排序、过滤等变化
|
||||
|
@ -60,6 +86,8 @@ const useTableParams = (
|
|||
NonNullable<TableProps<IMAGES.ImageItem>['onChange']>
|
||||
>(
|
||||
(pagination, filters, sorter, extra) => {
|
||||
console.log('handleTableChange',pagination,filters,sorter,extra);
|
||||
|
||||
// 过滤掉空值的filters
|
||||
const filteredFilters: Record<string, any> = {};
|
||||
Object.entries(filters || {}).forEach(([key, value]) => {
|
||||
|
@ -104,7 +132,7 @@ const useTableParams = (
|
|||
return {
|
||||
tableParams,
|
||||
getApiParams,
|
||||
updateParams,
|
||||
updateParams, // 统一的更新方法
|
||||
handleTableChange,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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 <Tooltip>{IMAGES_TYPE_MAP[key] || '--'}</Tooltip>;
|
||||
},
|
||||
defaultVisible: true,
|
||||
filters: [
|
||||
{ text: '全部', value: '全部' },
|
||||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm }) => (
|
||||
<Menu
|
||||
selectedKeys={selectedKeys.length > 0 ? selectedKeys : ['全部']}
|
||||
onClick={({ key }) => {
|
||||
setSelectedKeys(key === '全部' ? [] : [key]);
|
||||
confirm({ closeDropdown: true }); // 立即触发筛选并关闭下拉菜单
|
||||
}}
|
||||
items={[
|
||||
{ key: '全部', label: '全部' },
|
||||
...Object.entries(IMAGES_TYPE_MAP).map(([key, value]) => ({
|
||||
text: value,
|
||||
value: key, // 保持 key 为字符串
|
||||
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({
|
||||
|
@ -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({
|
||||
search: {
|
||||
image_name: searchValue,
|
||||
},
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: currentTableParams.pagination?.pageSize || 10,
|
||||
},
|
||||
filters: {
|
||||
...currentTableParams.filters,
|
||||
image_name: searchValue ? searchValue : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
[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 (
|
||||
<div className="image-list">
|
||||
|
@ -432,18 +487,9 @@ const ImageList: React.FC = () => {
|
|||
<Input.Search
|
||||
placeholder="镜像名称"
|
||||
value={searchText}
|
||||
onChange={(e) => {
|
||||
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}
|
||||
/>
|
||||
<Button
|
||||
onClick={handleRefresh}
|
||||
|
|
|
@ -17,6 +17,9 @@ export async function delImagesAPI(params:any) {
|
|||
return request<any>(`${BASE_URL}/image/delete`, {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -34,5 +37,8 @@ export async function cancelUploadImagesAPI(params:any) {
|
|||
return request<any>(`${BASE_URL_FILE}/cancel/upload`, {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
});
|
||||
}
|
|
@ -22,16 +22,14 @@ declare namespace IMAGES {
|
|||
pageSize: number;
|
||||
total?: number;
|
||||
};
|
||||
image_name?: string;
|
||||
image_type?: number | string; // 修改为联合类型
|
||||
filters?: Record<string, any>;
|
||||
search?: Record<string, any>; // 增加搜索参数
|
||||
filters?: Record<string, any>; // 表格搜索参数
|
||||
sort?: {
|
||||
field?: string;
|
||||
order?: 'ascend' | 'descend';
|
||||
};
|
||||
sortOrder?: 'ascend' | 'descend' | null;
|
||||
sortField?: string | number | null;
|
||||
image_status?: string;
|
||||
}
|
||||
|
||||
interface Images_ListInfo {
|
||||
|
|
Loading…
Reference in New Issue