From f210c029b661341579030baf6a38c2e02aaf88de Mon Sep 17 00:00:00 2001 From: chengkai3 Date: Sun, 28 Jun 2026 15:30:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:[FL-207][=E6=81=A2=E5=A4=8D=E9=81=BF?= =?UTF-8?q?=E9=9B=B7=E5=99=A8=E7=BB=84=E5=90=88=E5=B1=82=E7=BA=A7=E5=B9=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=A8=A1=E5=9E=8B=E6=96=87=E4=BB=B6=E4=B8=8B?= =?UTF-8?q?=E9=92=BB]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: multica-agent --- web/src/app/admin/atp-models/page.tsx | 105 ++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/web/src/app/admin/atp-models/page.tsx b/web/src/app/admin/atp-models/page.tsx index 3c3429f..100815c 100644 --- a/web/src/app/admin/atp-models/page.tsx +++ b/web/src/app/admin/atp-models/page.tsx @@ -31,7 +31,7 @@ import { useAuth } from "@/components/auth-provider"; import { useMobileDetection } from "@/hooks/use-mobile-detection"; import { useToastFeedback } from "@/hooks/use-toast-feedback"; import { readApiError } from "@/lib/api"; -import type { AtpAssetListResponse, AtpAssetSummary } from "@/types/auth"; +import type { AtpAssetListResponse, AtpAssetSummary, AtpAssetFileEntry } from "@/types/auth"; const AntCard = Card as unknown as ComponentType>; @@ -528,8 +528,28 @@ export default function AtpModelsPage() { displayName: string; value: string; item?: AtpAssetSummary; + isDir?: boolean; }; + const [selectedAssetForFiles, setSelectedAssetForFiles] = useState(null); + const [currentFilePath, setCurrentFilePath] = useState(""); + + const filesQuery = useQuery({ + queryKey: ["atp-asset-files", selectedAssetForFiles], + enabled: Boolean(user && canRead && selectedAssetForFiles && fileViewPath.length >= 5), + queryFn: async () => { + if (!selectedAssetForFiles) return null; + const asset = assetItems.find((item) => item.id === selectedAssetForFiles); + if (!asset || !asset.active_release_id) return null; + + const response = await fetchWithAuth(`/api/v1/atp/assets/${selectedAssetForFiles}/releases/${asset.active_release_id}/files`); + if (!response.ok) { + throw new Error(await readApiError(response)); + } + return (await response.json()) as { items: Array<{ relative_path: string; name: string; is_dir: boolean }> }; + }, + }); + const getFileViewItems = useCallback((): FileViewItem[] => { const currentLevel = fileViewPath.length; @@ -596,13 +616,41 @@ export default function AtpModelsPage() { const voltage = fileViewPath[0]; const tower = fileViewPath[1]; const scene = fileViewPath[2]; - return assetItems + const arresterSet = new Set(); + assetItems .filter( (item) => (item.voltage_level || "未分类") === voltage && (item.tower_type || "未分类") === tower && (item.scene_type || "未分类") === scene ) + .forEach((item) => { + const arrester = item.arrester_config || "未分类"; + arresterSet.add(arrester); + }); + return Array.from(arresterSet) + .sort((a, b) => a.localeCompare(b, "zh-CN")) + .map((arrester) => ({ + type: "folder" as const, + name: arrester, + displayName: formatDimensionValue(arrester, DEFAULT_ARRESTER_CONFIGS), + value: arrester, + })); + } + + if (currentLevel === 4) { + const voltage = fileViewPath[0]; + const tower = fileViewPath[1]; + const scene = fileViewPath[2]; + const arrester = fileViewPath[3]; + return assetItems + .filter( + (item) => + (item.voltage_level || "未分类") === voltage && + (item.tower_type || "未分类") === tower && + (item.scene_type || "未分类") === scene && + (item.arrester_config || "未分类") === arrester + ) .map((item) => ({ type: "folder" as const, name: item.name, @@ -612,14 +660,53 @@ export default function AtpModelsPage() { })); } + if (currentLevel >= 5) { + const assetId = fileViewPath[4]; + if (selectedAssetForFiles !== assetId) { + setSelectedAssetForFiles(assetId); + return []; + } + + if (filesQuery.isLoading || !filesQuery.data) { + return []; + } + + const pathInAsset = fileViewPath.slice(5).join("/"); + const items = filesQuery.data.items || []; + + return items + .filter((file) => { + if (pathInAsset === "") { + return !file.relative_path.includes("/") || file.relative_path.split("/").length === 1; + } + const prefix = pathInAsset + "/"; + if (!file.relative_path.startsWith(prefix)) { + return false; + } + const remainder = file.relative_path.substring(prefix.length); + return !remainder.includes("/") || remainder.split("/").length === 1; + }) + .map((file) => ({ + type: file.is_dir ? ("folder" as const) : ("file" as const), + name: file.name, + displayName: file.name, + value: file.relative_path, + isDir: file.is_dir, + })); + } + return []; - }, [assetItems, fileViewPath]); + }, [assetItems, fileViewPath, selectedAssetForFiles, filesQuery.data, filesQuery.isLoading]); const fileViewItems = useMemo(() => getFileViewItems(), [getFileViewItems]); const handleFileViewItemClick = (item: FileViewItem) => { if (item.type === "folder") { - setFileViewPath([...fileViewPath, item.value]); + if (fileViewPath.length >= 5 && item.isDir) { + setFileViewPath([...fileViewPath, item.name]); + } else { + setFileViewPath([...fileViewPath, item.value]); + } } }; @@ -635,10 +722,14 @@ export default function AtpModelsPage() { if (index === 0) return formatDimensionValue(fileViewPath[0], DEFAULT_VOLTAGE_LEVELS); if (index === 1) return formatDimensionValue(fileViewPath[1], DEFAULT_TOWER_TYPES); if (index === 2) return formatDimensionValue(fileViewPath[2], DEFAULT_SCENE_TYPES); - if (index === 3) { - const modelId = fileViewPath[3]; + if (index === 3) return formatDimensionValue(fileViewPath[3], DEFAULT_ARRESTER_CONFIGS); + if (index === 4) { + const modelId = fileViewPath[4]; const model = assetItems.find((item) => item.id === modelId); - return model ? model.name : fileViewPath[3]; + return model ? model.name : fileViewPath[4]; + } + if (index >= 5) { + return fileViewPath[index]; } return ""; };