Files
dmt-modelLibrary/web/src/pages/app/modules/folders.ts
2026-05-25 18:41:25 +08:00

133 lines
4.5 KiB
TypeScript

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<void>) {
try {
await action();
} catch (error) {
notifyError(error);
}
}
export async function loadFolders() {
const result = await api<FolderTreeResponse>("/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();
}