fix(镜像列表): 取消上传+参数逻辑
parent
0cae7e90bd
commit
a1dbc6a736
|
@ -11,7 +11,6 @@ import {
|
||||||
import {
|
import {
|
||||||
getChunkSize,
|
getChunkSize,
|
||||||
calculateMD5InChunks,
|
calculateMD5InChunks,
|
||||||
calculateMD5InChunksWorkers,
|
|
||||||
formatTime,
|
formatTime,
|
||||||
} from '@/pages/images/utils/images';
|
} from '@/pages/images/utils/images';
|
||||||
|
|
||||||
|
@ -400,7 +399,9 @@ const ImportModal: React.FC<IMAGES.ImportModalProps> = ({
|
||||||
// 如果有正在上传的文件,调用后端取消上传API
|
// 如果有正在上传的文件,调用后端取消上传API
|
||||||
if (fileId.current) {
|
if (fileId.current) {
|
||||||
try {
|
try {
|
||||||
await cancelUploadImagesAPI({ file_id: fileId.current });
|
const params = new URLSearchParams();
|
||||||
|
params.append('file_id', fileId.current);
|
||||||
|
await cancelUploadImagesAPI(params);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('取消上传API调用失败:', error);
|
console.error('取消上传API调用失败:', error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,9 @@ import { useCallback, useState } from 'react';
|
||||||
const useTableParams = (
|
const useTableParams = (
|
||||||
initialParams: IMAGES.TableParams = {
|
initialParams: IMAGES.TableParams = {
|
||||||
pagination: { current: 1, pageSize: 10 },
|
pagination: { current: 1, pageSize: 10 },
|
||||||
filters: {},
|
filters: {},// 表格的搜索对象
|
||||||
sort: {},
|
sort: {},
|
||||||
|
search: {}, // 添加搜索参数对象
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const [tableParams, setTableParams] =
|
const [tableParams, setTableParams] =
|
||||||
|
@ -13,8 +14,8 @@ const useTableParams = (
|
||||||
|
|
||||||
const getApiParams = useCallback(() => {
|
const getApiParams = useCallback(() => {
|
||||||
console.log('getApiParams', tableParams);
|
console.log('getApiParams', tableParams);
|
||||||
|
|
||||||
const { pagination, filters, sort, ...rest } = tableParams;
|
const { pagination, filters, sort, search, ...rest } = tableParams;
|
||||||
const apiParams: Record<string, any> = {
|
const apiParams: Record<string, any> = {
|
||||||
page_size: pagination?.pageSize,
|
page_size: pagination?.pageSize,
|
||||||
page_num: pagination?.current,
|
page_num: pagination?.current,
|
||||||
|
@ -26,27 +27,52 @@ const useTableParams = (
|
||||||
apiParams.order = sort.order === 'ascend' ? 'asc' : 'desc';
|
apiParams.order = sort.order === 'ascend' ? 'asc' : 'desc';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理表格搜索参数
|
||||||
Object.entries(filters || {}).forEach(([key, value]) => {
|
Object.entries(filters || {}).forEach(([key, value]) => {
|
||||||
if (value !== undefined && value !== null) {
|
if (value !== undefined && value !== null) {
|
||||||
apiParams[key] = value;
|
apiParams[key] = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 处理搜索参数
|
||||||
|
Object.entries(search || {}).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== null && value !== '') {
|
||||||
|
apiParams[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
console.log('getApiParams apiParams', apiParams);
|
console.log('getApiParams apiParams', apiParams);
|
||||||
return apiParams;
|
return apiParams;
|
||||||
}, [tableParams]);
|
}, [tableParams]);
|
||||||
|
|
||||||
const updateParams = useCallback((newParams: Partial<IMAGES.TableParams>) => {
|
// 统一的更新方法,可以处理所有参数类型
|
||||||
console.log('updateParams', newParams);
|
const updateParams = useCallback(
|
||||||
setTableParams((prev) => ({
|
(
|
||||||
...prev,
|
newParams: Partial<IMAGES.TableParams>,
|
||||||
...newParams,
|
options?: { resetPage?: boolean },
|
||||||
pagination: {
|
) => {
|
||||||
...prev.pagination,
|
console.log('updateParams', newParams);
|
||||||
...newParams.pagination,
|
|
||||||
},
|
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
|
* @returns void
|
||||||
* */
|
* */
|
||||||
const handleTableChange = useCallback<
|
const handleTableChange = useCallback<
|
||||||
NonNullable<TableProps<IMAGES.ImageItem>['onChange']>
|
NonNullable<TableProps<IMAGES.ImageItem>['onChange']>
|
||||||
>(
|
>(
|
||||||
(pagination, filters, sorter, extra) => {
|
(pagination, filters, sorter, extra) => {
|
||||||
|
console.log('handleTableChange',pagination,filters,sorter,extra);
|
||||||
|
|
||||||
// 过滤掉空值的filters
|
// 过滤掉空值的filters
|
||||||
const filteredFilters: Record<string, any> = {};
|
const filteredFilters: Record<string, any> = {};
|
||||||
Object.entries(filters || {}).forEach(([key, value]) => {
|
Object.entries(filters || {}).forEach(([key, value]) => {
|
||||||
|
@ -104,7 +132,7 @@ const useTableParams = (
|
||||||
return {
|
return {
|
||||||
tableParams,
|
tableParams,
|
||||||
getApiParams,
|
getApiParams,
|
||||||
updateParams,
|
updateParams, // 统一的更新方法
|
||||||
handleTableChange,
|
handleTableChange,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
Button,
|
Button,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Input,
|
Input,
|
||||||
|
Menu,
|
||||||
message,
|
message,
|
||||||
Modal,
|
Modal,
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
|
@ -38,6 +39,7 @@ type ColumnConfig = {
|
||||||
alwaysVisible?: boolean; // 始终显示的列
|
alwaysVisible?: boolean; // 始终显示的列
|
||||||
filters?: { text: string; value: string }[];
|
filters?: { text: string; value: string }[];
|
||||||
filterMultiple?: boolean; // 是否多选过滤
|
filterMultiple?: boolean; // 是否多选过滤
|
||||||
|
filterDropdown?: (props: any) => React.ReactNode;
|
||||||
defaultFilteredValue?: string[]; // 默认过滤值
|
defaultFilteredValue?: string[]; // 默认过滤值
|
||||||
onFilter?: (value: string, record: any) => boolean;
|
onFilter?: (value: string, record: any) => boolean;
|
||||||
};
|
};
|
||||||
|
@ -52,13 +54,33 @@ type TableColumn = {
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 在组件顶部添加防抖函数
|
// 在组件顶部添加防抖函数(支持取消)
|
||||||
const debounce = (func: Function, delay: number) => {
|
// 增强版防抖函数
|
||||||
let timer: NodeJS.Timeout;
|
const debounce = (func: Function, delay: number, immediate = false) => {
|
||||||
return (...args: any[]) => {
|
let timer: NodeJS.Timeout | null = null;
|
||||||
clearTimeout(timer);
|
const debounced = (...args: any[]) => {
|
||||||
timer = setTimeout(() => func(...args), delay);
|
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 ImageList: React.FC = () => {
|
||||||
|
@ -78,6 +100,7 @@ const ImageList: React.FC = () => {
|
||||||
current: 1,
|
current: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
},
|
},
|
||||||
|
search: {}, // 初始化搜索参数
|
||||||
});
|
});
|
||||||
|
|
||||||
// 在组件顶部添加一个 ref 来保存最新的 tableParams
|
// 在组件顶部添加一个 ref 来保存最新的 tableParams
|
||||||
|
@ -94,7 +117,8 @@ const ImageList: React.FC = () => {
|
||||||
tableParams.pagination?.pageSize,
|
tableParams.pagination?.pageSize,
|
||||||
tableParams?.sortOrder,
|
tableParams?.sortOrder,
|
||||||
tableParams?.sortField,
|
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>;
|
return <Tooltip>{IMAGES_TYPE_MAP[key] || '--'}</Tooltip>;
|
||||||
},
|
},
|
||||||
defaultVisible: true,
|
defaultVisible: true,
|
||||||
filters: [
|
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm }) => (
|
||||||
{ text: '全部', value: '全部' },
|
<Menu
|
||||||
...Object.entries(IMAGES_TYPE_MAP).map(([key, value]) => ({
|
selectedKeys={selectedKeys.length > 0 ? selectedKeys : ['全部']}
|
||||||
text: value,
|
onClick={({ key }) => {
|
||||||
value: key, // 保持 key 为字符串
|
setSelectedKeys(key === '全部' ? [] : [key]);
|
||||||
})),
|
confirm({ closeDropdown: true }); // 立即触发筛选并关闭下拉菜单
|
||||||
],
|
}}
|
||||||
|
items={[
|
||||||
|
{ key: '全部', label: '全部' },
|
||||||
|
...Object.entries(IMAGES_TYPE_MAP).map(([key, value]) => ({
|
||||||
|
key,
|
||||||
|
label: value,
|
||||||
|
})),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
),
|
||||||
filterMultiple: false,
|
filterMultiple: false,
|
||||||
defaultFilteredValue: ['全部'],
|
defaultFilteredValue: ['全部'],
|
||||||
// onFilter: (value, record) => {
|
|
||||||
// // 当选择"全部"时显示所有记录
|
|
||||||
// if (value === '全部') {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// // 比较时将字符串 value 转换为数字
|
|
||||||
// return record.image_type === Number(value);
|
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'storage_path',
|
key: 'storage_path',
|
||||||
|
@ -263,14 +288,12 @@ const ImageList: React.FC = () => {
|
||||||
const apiParams = {
|
const apiParams = {
|
||||||
...getApiParams(),
|
...getApiParams(),
|
||||||
};
|
};
|
||||||
if (searchInputRef.current) {
|
console.log('apiParams', apiParams);
|
||||||
apiParams.image_name = searchInputRef.current;
|
|
||||||
}
|
|
||||||
const imagesRes = await getImagesList(apiParams);
|
const imagesRes = await getImagesList(apiParams);
|
||||||
if (imagesRes.code == CODE) {
|
if (imagesRes.code == CODE) {
|
||||||
setImages(imagesRes.data?.data || []);
|
setImages(imagesRes.data?.data || []);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
console.log('imagesRes', imagesRes);
|
|
||||||
|
|
||||||
// 正确处理后端返回的分页信息
|
// 正确处理后端返回的分页信息
|
||||||
updateParams({
|
updateParams({
|
||||||
|
@ -278,7 +301,7 @@ const ImageList: React.FC = () => {
|
||||||
...tableParams.pagination,
|
...tableParams.pagination,
|
||||||
current: imagesRes.data?.page_num || 1,
|
current: imagesRes.data?.page_num || 1,
|
||||||
total: imagesRes.data?.total || 0,
|
total: imagesRes.data?.total || 0,
|
||||||
pageSize: tableParams.pagination.pageSize|| 10,
|
pageSize: tableParams.pagination.pageSize || 10,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -305,11 +328,13 @@ const ImageList: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = (record: IMAGES.ImageItem) => {
|
const handleDelete = (record: IMAGES.ImageItem) => {
|
||||||
|
console.log(record);
|
||||||
|
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '确认删除',
|
title: '确认删除',
|
||||||
content: `确定要删除镜像 "${record.image_name}" 吗?`,
|
content: `确定要删除镜像 "${record.image_name}" 吗?`,
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
delImagesAPI(record.id).then((res) => {
|
delImagesAPI({id:record.id}).then((res) => {
|
||||||
if (res.code == CODE) {
|
if (res.code == CODE) {
|
||||||
message.success('删除成功');
|
message.success('删除成功');
|
||||||
loadImages();
|
loadImages();
|
||||||
|
@ -399,30 +424,60 @@ const ImageList: React.FC = () => {
|
||||||
|
|
||||||
const handleSearch = useCallback(
|
const handleSearch = useCallback(
|
||||||
(searchValue: string) => {
|
(searchValue: string) => {
|
||||||
console.log('handleSearch', searchValue, tableParams);
|
// 只有当搜索值变化时才发送请求
|
||||||
|
// if (searchInputRef.current === searchValue) return;
|
||||||
|
|
||||||
|
console.log('实际触发搜索:', searchValue);
|
||||||
|
// searchInputRef.current = searchValue;
|
||||||
|
|
||||||
// 使用 ref 中的最新值
|
|
||||||
const currentTableParams = tableParamsRef.current;
|
const currentTableParams = tableParamsRef.current;
|
||||||
|
updateParams({
|
||||||
// 只有当搜索值变化时才更新参数和触发请求
|
search: {
|
||||||
if (searchInputRef.current !== searchValue) {
|
image_name: searchValue,
|
||||||
searchInputRef.current = searchValue;
|
},
|
||||||
updateParams({
|
pagination: {
|
||||||
pagination: {
|
current: 1,
|
||||||
current: 1,
|
pageSize: currentTableParams.pagination?.pageSize || 10,
|
||||||
pageSize: currentTableParams.pagination?.pageSize || 10,
|
},
|
||||||
},
|
});
|
||||||
filters: {
|
|
||||||
...currentTableParams.filters,
|
|
||||||
image_name: searchValue ? searchValue : undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[updateParams],
|
[updateParams],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 防抖版本(500ms延迟,不立即执行)
|
||||||
const debouncedSearch = useRef(debounce(handleSearch, 500)).current;
|
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 (
|
return (
|
||||||
<div className="image-list">
|
<div className="image-list">
|
||||||
|
@ -432,18 +487,9 @@ const ImageList: React.FC = () => {
|
||||||
<Input.Search
|
<Input.Search
|
||||||
placeholder="镜像名称"
|
placeholder="镜像名称"
|
||||||
value={searchText}
|
value={searchText}
|
||||||
onChange={(e) => {
|
onChange={(e) => handleSearchChange(e.target.value)}
|
||||||
const value = e.target.value;
|
|
||||||
setSearchText(value);
|
|
||||||
// 只有当输入为空时立即触发搜索,否则使用防抖
|
|
||||||
if (value === '') {
|
|
||||||
handleSearch('');
|
|
||||||
} else {
|
|
||||||
debouncedSearch(value);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{ width: 300 }}
|
style={{ width: 300 }}
|
||||||
onSearch={handleSearch}
|
onSearch={handleEnterSearch}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleRefresh}
|
onClick={handleRefresh}
|
||||||
|
|
|
@ -17,6 +17,9 @@ export async function delImagesAPI(params:any) {
|
||||||
return request<any>(`${BASE_URL}/image/delete`, {
|
return request<any>(`${BASE_URL}/image/delete`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: params,
|
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`, {
|
return request<any>(`${BASE_URL_FILE}/cancel/upload`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: params,
|
data: params,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -22,16 +22,14 @@ declare namespace IMAGES {
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
total?: number;
|
total?: number;
|
||||||
};
|
};
|
||||||
image_name?: string;
|
search?: Record<string, any>; // 增加搜索参数
|
||||||
image_type?: number | string; // 修改为联合类型
|
filters?: Record<string, any>; // 表格搜索参数
|
||||||
filters?: Record<string, any>;
|
|
||||||
sort?: {
|
sort?: {
|
||||||
field?: string;
|
field?: string;
|
||||||
order?: 'ascend' | 'descend';
|
order?: 'ascend' | 'descend';
|
||||||
};
|
};
|
||||||
sortOrder?: 'ascend' | 'descend' | null;
|
sortOrder?: 'ascend' | 'descend' | null;
|
||||||
sortField?: string | number | null;
|
sortField?: string | number | null;
|
||||||
image_status?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Images_ListInfo {
|
interface Images_ListInfo {
|
||||||
|
|
Loading…
Reference in New Issue