Files
MyGoNavi/frontend/src/utils/externalSqlTree.ts
Syngnat e6dd986115 feat(external-sql): 完善外部 SQL 目录文件管理
- 新增外部 SQL 文件的新建、重命名、删除和目录管理接口

- 后端限制 SQL 目录只加载 .sql 文件并补充目录操作测试

- 前端补齐 Wails 类型、浏览器 mock 和外部 SQL 树过滤逻辑

- 支持从外部 SQL 文件标签定位到侧栏目录节点
2026-06-02 11:15:30 +08:00

142 lines
4.6 KiB
TypeScript

import type { ExternalSQLDirectory, ExternalSQLTreeEntry } from '../types';
export type ExternalSQLNodeType =
| 'external-sql-root'
| 'external-sql-directory'
| 'external-sql-folder'
| 'external-sql-file';
export interface ExternalSQLTreeNode {
title: string;
key: string;
isLeaf?: boolean;
children?: ExternalSQLTreeNode[];
type: ExternalSQLNodeType;
dataRef: Record<string, unknown>;
}
type BuildExternalSQLRootNodeParams = {
dbNodeKey?: string;
connectionId?: string;
dbName?: string;
directories: ExternalSQLDirectory[];
directoryTrees: Record<string, ExternalSQLTreeEntry[]>;
};
const normalizeExternalSQLPath = (value: string): string =>
String(value || '').trim().replace(/\\/g, '/');
const resolveDirectoryDisplayName = (directory: ExternalSQLDirectory): string => {
const explicitName = String(directory.name || '').trim();
if (explicitName) return explicitName;
const normalizedPath = normalizeExternalSQLPath(directory.path);
const segments = normalizedPath.split('/').filter(Boolean);
return segments[segments.length - 1] || 'SQL目录';
};
export const buildExternalSQLDirectoryId = (connectionId: string, dbName: string, directoryPath: string): string =>
`external-sql-dir:${normalizeExternalSQLPath(directoryPath)}`;
export const buildExternalSQLTabId = (connectionId: string, dbName: string, filePath: string): string =>
`external-sql-tab:${String(connectionId || '').trim()}:${String(dbName || '').trim()}:${normalizeExternalSQLPath(filePath)}`;
const buildExternalSQLNodeKey = (type: ExternalSQLNodeType, base: string): string =>
`${type}:${normalizeExternalSQLPath(base)}`;
const isExternalSQLFileEntry = (entry: ExternalSQLTreeEntry): boolean => {
const name = String(entry.name || '').trim();
const path = normalizeExternalSQLPath(entry.path);
return /\.sql$/i.test(name) || /\.sql$/i.test(path);
};
const mapExternalSQLTreeEntries = (
entries: ExternalSQLTreeEntry[],
context: { connectionId: string; dbName: string; dbNodeKey: string; directoryId: string },
): ExternalSQLTreeNode[] => entries.flatMap((entry): ExternalSQLTreeNode[] => {
const entryPath = normalizeExternalSQLPath(entry.path);
if (entry.isDir) {
const children = mapExternalSQLTreeEntries(entry.children || [], context);
return [{
title: entry.name,
key: buildExternalSQLNodeKey('external-sql-folder', entryPath),
type: 'external-sql-folder',
isLeaf: children.length === 0,
children: children.length > 0 ? children : undefined,
dataRef: {
connectionId: context.connectionId,
dbName: context.dbName,
dbNodeKey: context.dbNodeKey,
directoryId: context.directoryId,
path: entry.path,
name: entry.name,
},
}];
}
if (!isExternalSQLFileEntry(entry)) {
return [];
}
return [{
title: entry.name,
key: buildExternalSQLNodeKey('external-sql-file', entryPath),
type: 'external-sql-file',
isLeaf: true,
dataRef: {
connectionId: context.connectionId,
dbName: context.dbName,
dbNodeKey: context.dbNodeKey,
directoryId: context.directoryId,
path: entry.path,
name: entry.name,
},
}];
});
export const buildExternalSQLRootNode = ({
dbNodeKey = 'external-sql-root',
connectionId = '',
dbName = '',
directories,
directoryTrees,
}: BuildExternalSQLRootNodeParams): ExternalSQLTreeNode => {
const sortedDirectories = [...directories].sort((left, right) =>
resolveDirectoryDisplayName(left).toLowerCase().localeCompare(resolveDirectoryDisplayName(right).toLowerCase()),
);
const children = sortedDirectories.map((directory) => {
const directoryChildren = mapExternalSQLTreeEntries(directoryTrees[directory.id] || [], {
connectionId,
dbName,
dbNodeKey,
directoryId: directory.id,
});
return {
title: resolveDirectoryDisplayName(directory),
key: buildExternalSQLNodeKey('external-sql-directory', directory.id),
type: 'external-sql-directory' as const,
isLeaf: directoryChildren.length === 0,
children: directoryChildren.length > 0 ? directoryChildren : undefined,
dataRef: {
...directory,
connectionId,
dbName,
dbNodeKey,
},
};
});
return {
title: children.length > 0 ? `外部 SQL 目录 (${children.length})` : '外部 SQL 目录',
key: dbNodeKey === 'external-sql-root' ? 'external-sql-root' : `${dbNodeKey}-external-sql`,
type: 'external-sql-root',
isLeaf: children.length === 0,
children: children.length > 0 ? children : undefined,
dataRef: {
connectionId,
dbName,
dbNodeKey,
},
};
};