import $ from "jquery"; import "jstree"; import { api } from "../../../services/api"; import { getCurrentUser } from "../../../services/authState"; import type { FolderTreeResponse } from "../../../types"; import { confirmDialog, notify, notifyError, promptDialog } from "../../../ui/dialogs"; import { appState } from "../appState"; import { loadModels } from "./models"; type JsTreeNode = { id: string; text: string; }; function isRealFolderId(id: string) { return /^\d+$/.test(id); } function selectFolder(node: JsTreeNode) { appState.selectedFolderId = Number(node.id); appState.selectedFolderName = appState.folders.find((folder) => folder.id === Number(node.id))?.name ?? node.text; } async function createFolder(parentId: number | null) { const name = await promptDialog({ title: "新建目录", label: "目录名称" }); if (!name) return; await api("/api/folders", { method: "POST", body: JSON.stringify({ parentId, name }) }); await loadFolders(); } async function renameFolder(folderId: number | null) { if (!folderId) return notify("请先选择目录"); const current = appState.folders.find((folder) => folder.id === folderId); if (!current?.parent_id) return notify("根目录不能重命名"); const name = await promptDialog({ title: "重命名目录", label: "目录名称", value: current.name }); if (!name) return; await api(`/api/folders/${folderId}`, { method: "PUT", body: JSON.stringify({ name }) }); await loadFolders(); } async function deleteFolder(folderId: number | null) { if (!folderId) return notify("请先选择目录"); const current = appState.folders.find((folder) => folder.id === folderId); if (!current?.parent_id) return notify("根目录不能删除"); const confirmed = await confirmDialog("删除目录会删除目录下所有模型和子目录,是否继续?"); if (!confirmed) return; await api(`/api/folders/${folderId}`, { method: "DELETE" }); appState.selectedFolderId = null; await loadFolders(); } async function runFolderAction(action: () => Promise) { try { await action(); } catch (error) { notifyError(error); } } export async function loadFolders() { const result = await api("/api/folders"); appState.folders = result.folders; const root = appState.folders.find((folder) => folder.parent_id === null); appState.selectedFolderId ??= root?.id ?? null; appState.selectedFolderName = appState.folders.find((folder) => folder.id === appState.selectedFolderId)?.name ?? ""; $("#folderTree").jstree("destroy"); $("#folderTree").jstree({ core: { data: result.tree, multiple: false }, plugins: ["contextmenu"], contextmenu: { items(node: JsTreeNode) { if (!isRealFolderId(node.id)) return {}; const folderId = Number(node.id); const folder = appState.folders.find((item) => item.id === folderId); const canWrite = Boolean(folder?.permissions.write); const isSystem = Boolean(folder?.is_system); return { create: { label: "新建目录", icon: "tree-menu-icon tree-menu-icon-add", _disabled: !canWrite, action: () => runFolderAction(async () => { selectFolder(node); await createFolder(folderId); }) }, rename: { label: "重命名", icon: "tree-menu-icon tree-menu-icon-edit", _disabled: !canWrite || isSystem, action: () => runFolderAction(async () => { selectFolder(node); await renameFolder(folderId); }) }, remove: { label: "删除", icon: "tree-menu-icon tree-menu-icon-delete", _disabled: !canWrite || isSystem, action: () => runFolderAction(async () => { selectFolder(node); await deleteFolder(folderId); }) } }; } } }).on("select_node.jstree", async (_event: JQuery.Event, data: { node: { id: string; text: string } }) => { if (!isRealFolderId(data.node.id)) return; appState.selectedFolderId = Number(data.node.id); appState.selectedFolderName = appState.folders.find((folder) => folder.id === appState.selectedFolderId)?.name ?? data.node.text; appState.page = 1; await loadModels(); }); if (appState.selectedFolderId) { $("#folderTree").on("ready.jstree", () => { $("#folderTree").jstree(true).select_node(String(appState.selectedFolderId)); }); } await loadModels(); }