You've already forked DataMate
fix: reset pagination when switching operator market category filters (#205)
This commit is contained in:
@@ -1,236 +1,238 @@
|
|||||||
// 首页数据获取
|
// 首页数据获取
|
||||||
// 支持轮询功能,使用示例:
|
// 支持轮询功能,使用示例:
|
||||||
// const { fetchData, startPolling, stopPolling, isPolling } = useFetchData(
|
// const { fetchData, startPolling, stopPolling, isPolling } = useFetchData(
|
||||||
// fetchFunction,
|
// fetchFunction,
|
||||||
// mapFunction,
|
// mapFunction,
|
||||||
// 5000, // 5秒轮询一次,默认30秒
|
// 5000, // 5秒轮询一次,默认30秒
|
||||||
// true, // 是否自动开始轮询,默认 true
|
// true, // 是否自动开始轮询,默认 true
|
||||||
// [fetchStatistics, fetchOtherData] // 额外的轮询函数数组
|
// [fetchStatistics, fetchOtherData] // 额外的轮询函数数组
|
||||||
// );
|
// );
|
||||||
//
|
//
|
||||||
// startPolling(); // 开始轮询
|
// startPolling(); // 开始轮询
|
||||||
// stopPolling(); // 停止轮询
|
// stopPolling(); // 停止轮询
|
||||||
// 手动调用 fetchData() 时,如果正在轮询,会重新开始轮询计时
|
// 手动调用 fetchData() 时,如果正在轮询,会重新开始轮询计时
|
||||||
// 轮询时会同时执行主要的 fetchFunction 和所有额外的轮询函数
|
// 轮询时会同时执行主要的 fetchFunction 和所有额外的轮询函数
|
||||||
import { useState, useRef, useEffect, useCallback } from "react";
|
import { useState, useRef, useEffect, useCallback } from "react";
|
||||||
import { useDebouncedEffect } from "./useDebouncedEffect";
|
import { useDebouncedEffect } from "./useDebouncedEffect";
|
||||||
import Loading from "@/utils/loading";
|
import Loading from "@/utils/loading";
|
||||||
import { App } from "antd";
|
import { App } from "antd";
|
||||||
|
|
||||||
export default function useFetchData<T>(
|
export default function useFetchData<T>(
|
||||||
fetchFunc: (params?: any) => Promise<any>,
|
fetchFunc: (params?: any) => Promise<any>,
|
||||||
mapDataFunc: (data: Partial<T>) => T = (data) => data as T,
|
mapDataFunc: (data: Partial<T>) => T = (data) => data as T,
|
||||||
pollingInterval: number = 30000, // 默认30秒轮询一次
|
pollingInterval: number = 30000, // 默认30秒轮询一次
|
||||||
autoRefresh: boolean = false, // 是否自动开始轮询,默认 false
|
autoRefresh: boolean = false, // 是否自动开始轮询,默认 false
|
||||||
additionalPollingFuncs: (() => Promise<any>)[] = [], // 额外的轮询函数
|
additionalPollingFuncs: (() => Promise<any>)[] = [], // 额外的轮询函数
|
||||||
pageOffset: number = 1
|
pageOffset: number = 1
|
||||||
) {
|
) {
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
|
|
||||||
// 轮询相关状态
|
// 轮询相关状态
|
||||||
const [isPolling, setIsPolling] = useState(false);
|
const [isPolling, setIsPolling] = useState(false);
|
||||||
const pollingTimerRef = useRef<NodeJS.Timeout | null>(null);
|
const pollingTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
// 表格数据
|
// 表格数据
|
||||||
const [tableData, setTableData] = useState<T[]>([]);
|
const [tableData, setTableData] = useState<T[]>([]);
|
||||||
// 设置加载状态
|
// 设置加载状态
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
// 搜索参数
|
// 搜索参数
|
||||||
const [searchParams, setSearchParams] = useState({
|
const [searchParams, setSearchParams] = useState({
|
||||||
keyword: "",
|
keyword: "",
|
||||||
filter: {
|
filter: {
|
||||||
type: [] as string[],
|
type: [] as string[],
|
||||||
status: [] as string[],
|
status: [] as string[],
|
||||||
tags: [] as string[],
|
tags: [] as string[],
|
||||||
},
|
// 通用分类筛选(如算子市场的分类 ID 列表)
|
||||||
current: 1,
|
categories: [] as string[],
|
||||||
pageSize: 12,
|
},
|
||||||
});
|
current: 1,
|
||||||
|
pageSize: 12,
|
||||||
// 分页配置
|
});
|
||||||
const [pagination, setPagination] = useState({
|
|
||||||
total: 0,
|
// 分页配置
|
||||||
showSizeChanger: true,
|
const [pagination, setPagination] = useState({
|
||||||
pageSizeOptions: ["12", "24", "48"],
|
total: 0,
|
||||||
showTotal: (total: number) => `共 ${total} 条`,
|
showSizeChanger: true,
|
||||||
onChange: (current: number, pageSize?: number) => {
|
pageSizeOptions: ["12", "24", "48"],
|
||||||
setSearchParams((prev) => ({
|
showTotal: (total: number) => `共 ${total} 条`,
|
||||||
...prev,
|
onChange: (current: number, pageSize?: number) => {
|
||||||
current,
|
setSearchParams((prev) => ({
|
||||||
pageSize: pageSize || prev.pageSize,
|
...prev,
|
||||||
}));
|
current,
|
||||||
},
|
pageSize: pageSize || prev.pageSize,
|
||||||
});
|
}));
|
||||||
|
},
|
||||||
const handleFiltersChange = (searchFilters: { [key: string]: string[] }) => {
|
});
|
||||||
setSearchParams({
|
|
||||||
...searchParams,
|
const handleFiltersChange = (searchFilters: { [key: string]: string[] }) => {
|
||||||
current: 1,
|
setSearchParams({
|
||||||
filter: { ...searchParams.filter, ...searchFilters },
|
...searchParams,
|
||||||
});
|
current: 1,
|
||||||
};
|
filter: { ...searchParams.filter, ...searchFilters },
|
||||||
|
});
|
||||||
const handleKeywordChange = (keyword: string) => {
|
};
|
||||||
setSearchParams({
|
|
||||||
...searchParams,
|
const handleKeywordChange = (keyword: string) => {
|
||||||
current: 1,
|
setSearchParams({
|
||||||
keyword: keyword,
|
...searchParams,
|
||||||
});
|
current: 1,
|
||||||
};
|
keyword: keyword,
|
||||||
|
});
|
||||||
function getFirstOfArray(arr: string[]) {
|
};
|
||||||
if (!arr || arr.length === 0 || !Array.isArray(arr)) return undefined;
|
|
||||||
if (arr[0] === "all") return undefined;
|
function getFirstOfArray(arr: string[]) {
|
||||||
return arr[0];
|
if (!arr || arr.length === 0 || !Array.isArray(arr)) return undefined;
|
||||||
}
|
if (arr[0] === "all") return undefined;
|
||||||
|
return arr[0];
|
||||||
// 清除轮询定时器
|
}
|
||||||
const clearPollingTimer = useCallback(() => {
|
|
||||||
if (pollingTimerRef.current) {
|
// 清除轮询定时器
|
||||||
clearTimeout(pollingTimerRef.current);
|
const clearPollingTimer = useCallback(() => {
|
||||||
pollingTimerRef.current = null;
|
if (pollingTimerRef.current) {
|
||||||
}
|
clearTimeout(pollingTimerRef.current);
|
||||||
}, []);
|
pollingTimerRef.current = null;
|
||||||
|
}
|
||||||
const fetchData = useCallback(
|
}, []);
|
||||||
async (extraParams = {}, skipPollingRestart = false) => {
|
|
||||||
const { keyword, filter, current, pageSize } = searchParams;
|
const fetchData = useCallback(
|
||||||
if (!skipPollingRestart) {
|
async (extraParams = {}, skipPollingRestart = false) => {
|
||||||
Loading.show();
|
const { keyword, filter, current, pageSize } = searchParams;
|
||||||
setLoading(true);
|
if (!skipPollingRestart) {
|
||||||
}
|
Loading.show();
|
||||||
|
setLoading(true);
|
||||||
// 如果正在轮询且不是轮询触发的调用,先停止当前轮询
|
}
|
||||||
const wasPolling = isPolling && !skipPollingRestart;
|
|
||||||
if (wasPolling) {
|
// 如果正在轮询且不是轮询触发的调用,先停止当前轮询
|
||||||
clearPollingTimer();
|
const wasPolling = isPolling && !skipPollingRestart;
|
||||||
}
|
if (wasPolling) {
|
||||||
|
clearPollingTimer();
|
||||||
try {
|
}
|
||||||
// 同时执行主要数据获取和额外的轮询函数
|
|
||||||
const promises = [
|
try {
|
||||||
fetchFunc({
|
// 同时执行主要数据获取和额外的轮询函数
|
||||||
...Object.fromEntries(
|
const promises = [
|
||||||
Object.entries(filter).filter(([_, value]) => value != null && value.length > 0)
|
fetchFunc({
|
||||||
),
|
...Object.fromEntries(
|
||||||
...extraParams,
|
Object.entries(filter).filter(([_, value]) => value != null && value.length > 0)
|
||||||
keyword,
|
),
|
||||||
type: getFirstOfArray(filter?.type) || undefined,
|
...extraParams,
|
||||||
status: getFirstOfArray(filter?.status) || undefined,
|
keyword,
|
||||||
tags: filter?.tags?.length ? filter.tags.join(",") : undefined,
|
type: getFirstOfArray(filter?.type) || undefined,
|
||||||
page: current - pageOffset,
|
status: getFirstOfArray(filter?.status) || undefined,
|
||||||
size: pageSize, // Use camelCase for HTTP query params
|
tags: filter?.tags?.length ? filter.tags.join(",") : undefined,
|
||||||
}),
|
page: current - pageOffset,
|
||||||
...additionalPollingFuncs.map((func) => func()),
|
size: pageSize, // Use camelCase for HTTP query params
|
||||||
];
|
}),
|
||||||
|
...additionalPollingFuncs.map((func) => func()),
|
||||||
const results = await Promise.all(promises);
|
];
|
||||||
const { data } = results[0]; // 主要数据结果
|
|
||||||
|
const results = await Promise.all(promises);
|
||||||
setPagination((prev) => ({
|
const { data } = results[0]; // 主要数据结果
|
||||||
...prev,
|
|
||||||
total: data?.totalElements || 0,
|
setPagination((prev) => ({
|
||||||
}));
|
...prev,
|
||||||
let result = [];
|
total: data?.totalElements || 0,
|
||||||
if (mapDataFunc) {
|
}));
|
||||||
result = data?.content.map(mapDataFunc) ?? [];
|
let result = [];
|
||||||
}
|
if (mapDataFunc) {
|
||||||
setTableData(result);
|
result = data?.content.map(mapDataFunc) ?? [];
|
||||||
|
}
|
||||||
// 如果之前正在轮询且不是轮询触发的调用,重新开始轮询
|
setTableData(result);
|
||||||
if (wasPolling) {
|
|
||||||
const poll = () => {
|
// 如果之前正在轮询且不是轮询触发的调用,重新开始轮询
|
||||||
pollingTimerRef.current = setTimeout(() => {
|
if (wasPolling) {
|
||||||
fetchData({}, true).then(() => {
|
const poll = () => {
|
||||||
if (pollingTimerRef.current) {
|
pollingTimerRef.current = setTimeout(() => {
|
||||||
poll();
|
fetchData({}, true).then(() => {
|
||||||
}
|
if (pollingTimerRef.current) {
|
||||||
});
|
poll();
|
||||||
}, pollingInterval);
|
}
|
||||||
};
|
});
|
||||||
poll();
|
}, pollingInterval);
|
||||||
}
|
};
|
||||||
} catch (error) {
|
poll();
|
||||||
console.error(error);
|
}
|
||||||
message.error("数据获取失败,请稍后重试");
|
} catch (error) {
|
||||||
} finally {
|
console.error(error);
|
||||||
Loading.hide();
|
message.error("数据获取失败,请稍后重试");
|
||||||
setLoading(false);
|
} finally {
|
||||||
}
|
Loading.hide();
|
||||||
},
|
setLoading(false);
|
||||||
[
|
}
|
||||||
searchParams,
|
},
|
||||||
fetchFunc,
|
[
|
||||||
mapDataFunc,
|
searchParams,
|
||||||
isPolling,
|
fetchFunc,
|
||||||
clearPollingTimer,
|
mapDataFunc,
|
||||||
pollingInterval,
|
isPolling,
|
||||||
message,
|
clearPollingTimer,
|
||||||
additionalPollingFuncs,
|
pollingInterval,
|
||||||
]
|
message,
|
||||||
);
|
additionalPollingFuncs,
|
||||||
|
]
|
||||||
// 开始轮询
|
);
|
||||||
const startPolling = useCallback(() => {
|
|
||||||
clearPollingTimer();
|
// 开始轮询
|
||||||
setIsPolling(true);
|
const startPolling = useCallback(() => {
|
||||||
|
clearPollingTimer();
|
||||||
const poll = () => {
|
setIsPolling(true);
|
||||||
pollingTimerRef.current = setTimeout(() => {
|
|
||||||
fetchData({}, true).then(() => {
|
const poll = () => {
|
||||||
if (pollingTimerRef.current) {
|
pollingTimerRef.current = setTimeout(() => {
|
||||||
poll();
|
fetchData({}, true).then(() => {
|
||||||
}
|
if (pollingTimerRef.current) {
|
||||||
});
|
poll();
|
||||||
}, pollingInterval);
|
}
|
||||||
};
|
});
|
||||||
|
}, pollingInterval);
|
||||||
poll();
|
};
|
||||||
}, [pollingInterval, clearPollingTimer, fetchData]);
|
|
||||||
|
poll();
|
||||||
// 停止轮询
|
}, [pollingInterval, clearPollingTimer, fetchData]);
|
||||||
const stopPolling = useCallback(() => {
|
|
||||||
clearPollingTimer();
|
// 停止轮询
|
||||||
setIsPolling(false);
|
const stopPolling = useCallback(() => {
|
||||||
}, [clearPollingTimer]);
|
clearPollingTimer();
|
||||||
|
setIsPolling(false);
|
||||||
// 搜索参数变化时,自动刷新数据
|
}, [clearPollingTimer]);
|
||||||
// keyword 变化时,防抖500ms后刷新
|
|
||||||
useDebouncedEffect(
|
// 搜索参数变化时,自动刷新数据
|
||||||
() => {
|
// keyword 变化时,防抖500ms后刷新
|
||||||
fetchData();
|
useDebouncedEffect(
|
||||||
},
|
() => {
|
||||||
[searchParams],
|
fetchData();
|
||||||
searchParams?.keyword ? 500 : 0
|
},
|
||||||
);
|
[searchParams],
|
||||||
|
searchParams?.keyword ? 500 : 0
|
||||||
// 组件卸载时清理轮询
|
);
|
||||||
useEffect(() => {
|
|
||||||
if (autoRefresh) {
|
// 组件卸载时清理轮询
|
||||||
startPolling();
|
useEffect(() => {
|
||||||
}
|
if (autoRefresh) {
|
||||||
return () => {
|
startPolling();
|
||||||
clearPollingTimer();
|
}
|
||||||
};
|
return () => {
|
||||||
}, [clearPollingTimer]);
|
clearPollingTimer();
|
||||||
|
};
|
||||||
return {
|
}, [clearPollingTimer]);
|
||||||
loading,
|
|
||||||
tableData,
|
return {
|
||||||
pagination: {
|
loading,
|
||||||
...pagination,
|
tableData,
|
||||||
current: searchParams.current,
|
pagination: {
|
||||||
pageSize: searchParams.pageSize,
|
...pagination,
|
||||||
},
|
current: searchParams.current,
|
||||||
searchParams,
|
pageSize: searchParams.pageSize,
|
||||||
setSearchParams,
|
},
|
||||||
setPagination,
|
searchParams,
|
||||||
handleFiltersChange,
|
setSearchParams,
|
||||||
handleKeywordChange,
|
setPagination,
|
||||||
fetchData,
|
handleFiltersChange,
|
||||||
isPolling,
|
handleKeywordChange,
|
||||||
startPolling,
|
fetchData,
|
||||||
stopPolling,
|
isPolling,
|
||||||
};
|
startPolling,
|
||||||
}
|
stopPolling,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,215 +1,223 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Button, message } from "antd";
|
import { Button, message } from "antd";
|
||||||
import {
|
import {
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
FilterOutlined,
|
FilterOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
DownloadOutlined
|
DownloadOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Boxes } from "lucide-react";
|
import { Boxes } from "lucide-react";
|
||||||
import { SearchControls } from "@/components/SearchControls";
|
import { SearchControls } from "@/components/SearchControls";
|
||||||
import CardView from "@/components/CardView";
|
import CardView from "@/components/CardView";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import type {
|
import type {
|
||||||
CategoryTreeI,
|
CategoryTreeI,
|
||||||
OperatorI,
|
OperatorI,
|
||||||
} from "@/pages/OperatorMarket/operator.model";
|
} from "@/pages/OperatorMarket/operator.model";
|
||||||
import Filters from "./components/Filters";
|
import Filters from "./components/Filters";
|
||||||
import TagManagement from "@/components/business/TagManagement";
|
import TagManagement from "@/components/business/TagManagement";
|
||||||
import { ListView } from "./components/List";
|
import { ListView } from "./components/List";
|
||||||
import useFetchData from "@/hooks/useFetchData";
|
import useFetchData from "@/hooks/useFetchData";
|
||||||
import {
|
import {
|
||||||
deleteOperatorByIdUsingDelete,
|
deleteOperatorByIdUsingDelete,
|
||||||
downloadExampleOperatorUsingGet,
|
downloadExampleOperatorUsingGet,
|
||||||
queryCategoryTreeUsingGet,
|
queryCategoryTreeUsingGet,
|
||||||
queryOperatorsUsingPost,
|
queryOperatorsUsingPost,
|
||||||
} from "../operator.api";
|
} from "../operator.api";
|
||||||
import { mapOperator } from "../operator.const";
|
import { mapOperator } from "../operator.const";
|
||||||
|
|
||||||
export default function OperatorMarketPage() {
|
export default function OperatorMarketPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [viewMode, setViewMode] = useState<"card" | "list">("card");
|
const [viewMode, setViewMode] = useState<"card" | "list">("card");
|
||||||
|
|
||||||
const [selectedFilters, setSelectedFilters] = useState<
|
const [selectedFilters, setSelectedFilters] = useState<
|
||||||
Record<string, string[]>
|
Record<string, string[]>
|
||||||
>({});
|
>({});
|
||||||
|
|
||||||
const [showFilters, setShowFilters] = useState(true);
|
const [showFilters, setShowFilters] = useState(true);
|
||||||
const [categoriesTree, setCategoriesTree] = useState<CategoryTreeI[]>([]);
|
const [categoriesTree, setCategoriesTree] = useState<CategoryTreeI[]>([]);
|
||||||
|
|
||||||
const initCategoriesTree = async () => {
|
const initCategoriesTree = async () => {
|
||||||
const { data } = await queryCategoryTreeUsingGet({ page: 0, size: 1000 });
|
const { data } = await queryCategoryTreeUsingGet({ page: 0, size: 1000 });
|
||||||
setCategoriesTree(data.content || []);
|
setCategoriesTree(data.content || []);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initCategoriesTree();
|
initCategoriesTree();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tableData,
|
tableData,
|
||||||
pagination,
|
pagination,
|
||||||
searchParams,
|
searchParams,
|
||||||
fetchData,
|
setSearchParams,
|
||||||
handleFiltersChange,
|
fetchData,
|
||||||
handleKeywordChange,
|
handleFiltersChange,
|
||||||
} = useFetchData(queryOperatorsUsingPost, mapOperator);
|
handleKeywordChange,
|
||||||
|
} = useFetchData(queryOperatorsUsingPost, mapOperator);
|
||||||
const handleUploadOperator = () => {
|
|
||||||
navigate(`/data/operator-market/create`);
|
const handleUploadOperator = () => {
|
||||||
};
|
navigate(`/data/operator-market/create`);
|
||||||
|
};
|
||||||
const handleDownload = async () => {
|
|
||||||
await downloadExampleOperatorUsingGet("test_operator.tar");
|
const handleDownload = async () => {
|
||||||
message.success("文件下载成功");
|
await downloadExampleOperatorUsingGet("test_operator.tar");
|
||||||
};
|
message.success("文件下载成功");
|
||||||
|
};
|
||||||
const handleUpdateOperator = (operator: OperatorI) => {
|
|
||||||
navigate(`/data/operator-market/create/${operator.id}`);
|
const handleUpdateOperator = (operator: OperatorI) => {
|
||||||
};
|
navigate(`/data/operator-market/create/${operator.id}`);
|
||||||
|
};
|
||||||
const handleDeleteOperator = async (operator: OperatorI) => {
|
|
||||||
try {
|
const handleDeleteOperator = async (operator: OperatorI) => {
|
||||||
await deleteOperatorByIdUsingDelete(operator.id);
|
try {
|
||||||
message.success("算子删除成功");
|
await deleteOperatorByIdUsingDelete(operator.id);
|
||||||
fetchData();
|
message.success("算子删除成功");
|
||||||
} catch (error) {
|
fetchData();
|
||||||
message.error("算子删除失败");
|
} catch (error) {
|
||||||
}
|
message.error("算子删除失败");
|
||||||
};
|
}
|
||||||
|
};
|
||||||
const operations = [
|
|
||||||
{
|
const operations = [
|
||||||
key: "edit",
|
{
|
||||||
label: "更新",
|
key: "edit",
|
||||||
icon: <EditOutlined />,
|
label: "更新",
|
||||||
onClick: handleUpdateOperator,
|
icon: <EditOutlined />,
|
||||||
},
|
onClick: handleUpdateOperator,
|
||||||
{
|
},
|
||||||
key: "delete",
|
{
|
||||||
label: "删除",
|
key: "delete",
|
||||||
danger: true,
|
label: "删除",
|
||||||
icon: <DeleteOutlined />,
|
danger: true,
|
||||||
confirm: {
|
icon: <DeleteOutlined />,
|
||||||
title: "确认删除",
|
confirm: {
|
||||||
description: "此操作不可撤销,是否继续?",
|
title: "确认删除",
|
||||||
okText: "删除",
|
description: "此操作不可撤销,是否继续?",
|
||||||
okType: "danger",
|
okText: "删除",
|
||||||
cancelText: "取消",
|
okType: "danger",
|
||||||
},
|
cancelText: "取消",
|
||||||
onClick: handleDeleteOperator,
|
},
|
||||||
},
|
onClick: handleDeleteOperator,
|
||||||
];
|
},
|
||||||
|
];
|
||||||
useEffect(() => {
|
|
||||||
if (Object.keys(selectedFilters).length === 0) {
|
useEffect(() => {
|
||||||
return;
|
const filteredIds = Object.values(selectedFilters).reduce(
|
||||||
}
|
(acc, filter: string[]) => {
|
||||||
const filteredIds = Object.values(selectedFilters).reduce(
|
if (filter.length) {
|
||||||
(acc, filter: string[]) => {
|
acc.push(...filter);
|
||||||
if (filter.length) {
|
}
|
||||||
acc.push(...filter);
|
|
||||||
}
|
return acc;
|
||||||
|
},
|
||||||
return acc;
|
[]
|
||||||
},
|
);
|
||||||
[]
|
|
||||||
);
|
// 分类筛选变化时:
|
||||||
|
// 1. 将分类 ID 写入通用 searchParams.filter.categories,确保分页时条件不会丢失
|
||||||
fetchData({ categories: filteredIds?.length ? filteredIds : undefined });
|
// 2. 将页码重置为 1,避免从“全选”页的当前页跳入细分列表的同一页
|
||||||
}, [selectedFilters]);
|
setSearchParams((prev) => ({
|
||||||
|
...prev,
|
||||||
return (
|
current: 1,
|
||||||
<div className="h-full flex flex-col gap-4">
|
filter: {
|
||||||
{/* Header */}
|
...prev.filter,
|
||||||
<div className="flex justify-between">
|
categories: filteredIds,
|
||||||
<h1 className="text-xl font-bold text-gray-900">算子市场</h1>
|
},
|
||||||
<div className="flex gap-2">
|
}));
|
||||||
{/*<TagManagement />*/}
|
}, [selectedFilters, setSearchParams]);
|
||||||
<Button
|
|
||||||
icon={<DownloadOutlined />}
|
return (
|
||||||
onClick={handleDownload}
|
<div className="h-full flex flex-col gap-4">
|
||||||
>
|
{/* Header */}
|
||||||
下载示例算子
|
<div className="flex justify-between">
|
||||||
</Button>
|
<h1 className="text-xl font-bold text-gray-900">算子市场</h1>
|
||||||
<Button
|
<div className="flex gap-2">
|
||||||
type="primary"
|
{/*<TagManagement />*/}
|
||||||
icon={<PlusOutlined />}
|
<Button
|
||||||
onClick={handleUploadOperator}
|
icon={<DownloadOutlined />}
|
||||||
>
|
onClick={handleDownload}
|
||||||
上传算子
|
>
|
||||||
</Button>
|
下载示例算子
|
||||||
</div>
|
</Button>
|
||||||
</div>
|
<Button
|
||||||
{/* Main Content */}
|
type="primary"
|
||||||
<div className="flex-overflow-auto flex-row border-card">
|
icon={<PlusOutlined />}
|
||||||
<div
|
onClick={handleUploadOperator}
|
||||||
className={`border-r border-gray-100 transition-all duration-300 ${
|
>
|
||||||
showFilters
|
上传算子
|
||||||
? "translate-x-0 w-56"
|
</Button>
|
||||||
: "-translate-x-full w-0 opacity-0"
|
</div>
|
||||||
}`}
|
</div>
|
||||||
>
|
{/* Main Content */}
|
||||||
<Filters
|
<div className="flex-overflow-auto flex-row border-card">
|
||||||
hideFilter={() => setShowFilters(false)}
|
<div
|
||||||
categoriesTree={categoriesTree}
|
className={`border-r border-gray-100 transition-all duration-300 ${
|
||||||
selectedFilters={selectedFilters}
|
showFilters
|
||||||
setSelectedFilters={setSelectedFilters}
|
? "translate-x-0 w-56"
|
||||||
/>
|
: "-translate-x-full w-0 opacity-0"
|
||||||
</div>
|
}`}
|
||||||
<div className="flex-overflow-auto p-6 ">
|
>
|
||||||
<div className="flex w-full items-top gap-4 border-b border-gray-200 mb-4">
|
<Filters
|
||||||
{!showFilters && (
|
hideFilter={() => setShowFilters(false)}
|
||||||
<Button
|
categoriesTree={categoriesTree}
|
||||||
type="text"
|
selectedFilters={selectedFilters}
|
||||||
icon={<FilterOutlined />}
|
setSelectedFilters={setSelectedFilters}
|
||||||
onClick={() => setShowFilters(true)}
|
/>
|
||||||
/>
|
</div>
|
||||||
)}
|
<div className="flex-overflow-auto p-6 ">
|
||||||
<div className="flex-1 mb-4">
|
<div className="flex w-full items-top gap-4 border-b border-gray-200 mb-4">
|
||||||
<SearchControls
|
{!showFilters && (
|
||||||
searchTerm={searchParams.keyword}
|
<Button
|
||||||
onSearchChange={handleKeywordChange}
|
type="text"
|
||||||
searchPlaceholder="搜索算子名称、描述..."
|
icon={<FilterOutlined />}
|
||||||
filters={[]}
|
onClick={() => setShowFilters(true)}
|
||||||
onFiltersChange={handleFiltersChange}
|
/>
|
||||||
viewMode={viewMode}
|
)}
|
||||||
onViewModeChange={setViewMode}
|
<div className="flex-1 mb-4">
|
||||||
showViewToggle={true}
|
<SearchControls
|
||||||
onReload={fetchData}
|
searchTerm={searchParams.keyword}
|
||||||
/>
|
onSearchChange={handleKeywordChange}
|
||||||
</div>
|
searchPlaceholder="搜索算子名称、描述..."
|
||||||
</div>
|
filters={[]}
|
||||||
{/* Content */}
|
onFiltersChange={handleFiltersChange}
|
||||||
{tableData.length === 0 ? (
|
viewMode={viewMode}
|
||||||
<div className="text-center py-12">
|
onViewModeChange={setViewMode}
|
||||||
<Boxes className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
showViewToggle={true}
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
onReload={fetchData}
|
||||||
没有找到匹配的算子
|
/>
|
||||||
</h3>
|
</div>
|
||||||
<p className="text-gray-500">尝试调整筛选条件或搜索关键词</p>
|
</div>
|
||||||
</div>
|
{/* Content */}
|
||||||
) : (
|
{tableData.length === 0 ? (
|
||||||
<>
|
<div className="text-center py-12">
|
||||||
{viewMode === "card" ? (
|
<Boxes className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
||||||
<CardView
|
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||||
data={tableData}
|
没有找到匹配的算子
|
||||||
pagination={pagination}
|
</h3>
|
||||||
operations={operations}
|
<p className="text-gray-500">尝试调整筛选条件或搜索关键词</p>
|
||||||
onView={(item) => navigate(`/data/operator-market/plugin-detail/${item.id}`)}
|
</div>
|
||||||
/>
|
) : (
|
||||||
) : (
|
<>
|
||||||
<ListView
|
{viewMode === "card" ? (
|
||||||
operators={tableData}
|
<CardView
|
||||||
operations={operations}
|
data={tableData}
|
||||||
pagination={pagination}
|
pagination={pagination}
|
||||||
/>
|
operations={operations}
|
||||||
)}
|
onView={(item) => navigate(`/data/operator-market/plugin-detail/${item.id}`)}
|
||||||
</>
|
/>
|
||||||
)}
|
) : (
|
||||||
</div>
|
<ListView
|
||||||
</div>
|
operators={tableData}
|
||||||
</div>
|
operations={operations}
|
||||||
);
|
pagination={pagination}
|
||||||
}
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user