0
0

修改了重命名的bug

This commit is contained in:
xubing
2026-01-30 11:26:15 +08:00
parent ad34dca525
commit 10f6f34127
6 changed files with 264 additions and 905 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -52,11 +52,10 @@ class ProjectService {
* 文件名/文件夹名净化:保留大小写,允许中文、字母、数字、-、_、.
*/
sanitizeFileName(name) {
let fileName = name.trim(); // 移除 .toLowerCase() 以保留大小写
let fileName = name.trim();
// 1. 替换所有空格为下划线
fileName = fileName.replace(/\s+/g, '_');
// 2. 移除特殊字符
// 在正则中增加了 A-Z 以及 \. (注意:\u4e00-\u9fa5 匹配中文)
fileName = fileName.replace(/[^\u4e00-\u9fa5a-zA-Z0-9_\-\.]/g, '');
// 3. 确保不为空
if (fileName.length === 0) {
@@ -64,6 +63,31 @@ class ProjectService {
}
return fileName;
}
// =============== 查重工具方法 (新增) ===============
/** 检查项目名是否重复 */
isProjectNameExists(name) {
return this.projects.some(p => p.name === name);
}
/** 检查指定项目下的飞行器名是否重复 */
isAircraftNameExists(projectId, name) {
return this.aircrafts.some(a => a.projectId === projectId && a.name === name);
}
/** 检查指定飞行器下的容器名是否重复 */
isContainerNameExists(aircraftId, name) {
return this.containers.some(c => c.aircraftId === aircraftId && c.name === name);
}
/** 检查指定容器下的配置名或文件名是否重复 */
isConfigOrFolderConflict(containerId, name, fileName, excludeId) {
const configs = this.getConfigsByContainer(containerId);
const folders = this.getModuleFoldersByContainer(containerId);
// 检查显示名称冲突 (排除自身)
const nameConflict = configs.some(c => c.id !== excludeId && c.name === name) ||
folders.some(f => f.id !== excludeId && f.name === name);
// 检查磁盘文件名/文件夹名冲突 (排除自身)
const fileConflict = configs.some(c => c.id !== excludeId && c.fileName === fileName) ||
folders.some(f => f.id !== excludeId && f.localPath.split('/').pop() === fileName);
return { nameConflict, fileConflict };
}
// =============== 项目相关方法 ===============
getProjects() {
return this.projects;
@@ -98,12 +122,10 @@ class ProjectService {
const project = this.projects.find(p => p.id === projectId);
if (!project)
return false;
// 先把要删的 id 都算出来,再统一过滤
const relatedAircrafts = this.aircrafts.filter(a => a.projectId === projectId);
const aircraftIds = relatedAircrafts.map(a => a.id);
const relatedContainers = this.containers.filter(c => aircraftIds.includes(c.aircraftId));
const containerIds = relatedContainers.map(c => c.id);
// 真正删除数据
this.projects = this.projects.filter(p => p.id !== projectId);
this.aircrafts = this.aircrafts.filter(a => a.projectId !== projectId);
this.containers = this.containers.filter(c => !aircraftIds.includes(c.aircraftId));
@@ -127,7 +149,6 @@ class ProjectService {
projectId: projectId
};
this.aircrafts.push(newAircraft);
// 创建飞行器目录
await this.createAircraftDirectory(newAircraft);
return newId;
}
@@ -143,23 +164,15 @@ class ProjectService {
const aircraft = this.aircrafts.find(a => a.id === aircraftId);
if (!aircraft)
return false;
// ⚠️ 先算出要删的 containerIds
const relatedContainers = this.containers.filter(c => c.aircraftId === aircraftId);
const containerIds = relatedContainers.map(c => c.id);
// 删除飞机自身和容器
this.aircrafts = this.aircrafts.filter(a => a.id !== aircraftId);
this.containers = this.containers.filter(c => c.aircraftId !== aircraftId);
// 再删关联的 config 和 moduleFolder
this.configs = this.configs.filter(cfg => !containerIds.includes(cfg.containerId));
this.moduleFolders = this.moduleFolders.filter(folder => !containerIds.includes(folder.containerId));
return true;
}
/**
* 从已存在的磁盘目录导入飞行器
* 不会创建/删除任何文件,只在内存中补充 Aircraft / Container / ModuleFolder 数据
*/
async importAircraftFromExistingFolder(projectId, aircraftName) {
// 已存在同名飞行器直接返回
const existed = this.getAircraftsByProject(projectId).find(a => a.name === aircraftName);
if (existed) {
return existed.id;
@@ -177,10 +190,8 @@ class ProjectService {
}
const aircraftDir = path.join(projectPath, aircraftName);
if (!fs.existsSync(aircraftDir)) {
// 目录不存在就不再解析子目录
return newId;
}
// 每个子目录视为一个容器(排除 .git
const entries = await fs.promises.readdir(aircraftDir, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isDirectory())
@@ -192,12 +203,6 @@ class ProjectService {
return newId;
}
// =============== 容器相关方法 ===============
/**
* ⚠️ 这里不再做 .git 过滤:
* - 我们只会通过 createContainer / importContainerFromExistingFolder 创建容器
* - importContainerFromExistingFolder 内部已经排除了 .git
* 所以不会出现名字叫 ".git" 的容器
*/
getContainersByAircraft(aircraftId) {
return this.containers.filter(c => c.aircraftId === aircraftId);
}
@@ -213,7 +218,6 @@ class ProjectService {
};
this.containers.push(newContainer);
await this.createContainerDirectory(newContainer);
// UI 手动创建的容器,仍然保留“默认两个配置”的行为
await this.createDefaultConfigs(newContainer);
return newId;
}
@@ -234,15 +238,6 @@ class ProjectService {
this.moduleFolders = this.moduleFolders.filter(folder => folder.containerId !== containerId);
return true;
}
/**
* 从已存在的磁盘目录导入容器
*
* ✅ 你的最新需求:
* - 不创建“默认两个配置”
* - 自动扫描容器目录下的『子文件夹』
* - 每个子文件夹创建一个 ModuleFoldertype: 'local'
* - 排除 `.git` 子文件夹
*/
async importContainerFromExistingFolder(aircraftId, containerName) {
if (containerName === '.git') {
throw new Error('不能将 .git 导入为容器');
@@ -258,15 +253,9 @@ class ProjectService {
aircraftId
};
this.containers.push(container);
// 🚩 关键逻辑:扫描容器目录下的子文件夹 -> 创建 ModuleFolder
await this.scanContainerModuleFolders(container);
// 不再创建默认两个配置(不调用 createDefaultConfigs
// 也不创建目录(目录是 Git 克隆出来的,本来就存在)
return newId;
}
/**
* 扫描容器目录中的子文件夹(不含 .git将其作为本地模块文件夹记录
*/
async scanContainerModuleFolders(container) {
try {
const aircraft = this.aircrafts.find(a => a.id === container.aircraftId);
@@ -308,10 +297,8 @@ class ProjectService {
getConfig(configId) {
return this.configs.find(c => c.id === configId);
}
// src/panels/services/ProjectService.ts
async createConfig(name, containerId) {
const newId = this.generateUniqueId('cfg', this.configs);
// 使用净化后的文件名
const newFileName = this.sanitizeFileName(name);
const newConfig = {
id: newId,
@@ -320,15 +307,11 @@ class ProjectService {
containerId: containerId
};
this.configs.push(newConfig);
// 1. 首先确保父级容器目录存在
await this.ensureContainerDirectoryExists(containerId);
// 2. 获取文件的完整磁盘路径
const filePath = this.getConfigFilePath(newId);
if (filePath) {
try {
const fileUri = vscode.Uri.file(filePath);
// 3. 真实创建文件:写入一个空的 Uint8Array 即可创建一个空文件
// 如果你希望有默认内容,可以在这里自定义 TextEncoder().encode("...")
await vscode.workspace.fs.writeFile(fileUri, new Uint8Array());
console.log(`✅ 磁盘文件已真实创建: ${filePath}`);
}
@@ -338,9 +321,6 @@ class ProjectService {
}
return newId;
}
/**
* [修改] 同时更新配置显示名称和文件名称
*/
updateConfigName(configId, newName, newFileName) {
const config = this.configs.find(c => c.id === configId);
if (config) {
@@ -357,19 +337,14 @@ class ProjectService {
this.configs = this.configs.filter(c => c.id !== configId);
return true;
}
/**
* [新增] 重命名磁盘上的配置文件
*/
async renameConfigFileOnDisk(configId, newFileName) {
const config = this.configs.find(c => c.id === configId);
if (!config)
return false;
const oldFilePath = this.getConfigFilePath(configId);
// 为了计算新路径,临时使用新的文件名
const oldFileName = config.fileName;
config.fileName = newFileName;
const newFilePath = this.getConfigFilePath(configId);
// 恢复旧的文件名,因为 ProjectService.updateConfigName 会在 ConfigPanel 中调用
config.fileName = oldFileName;
if (!oldFilePath || !newFilePath) {
return false;
@@ -415,23 +390,16 @@ class ProjectService {
this.moduleFolders = this.moduleFolders.filter(f => f.id !== folderId);
return true;
}
// =============== 文件系统操作 (新增/修改) ===============
/**
* 获取项目目录重命名所需的旧路径和新路径(新增)
*/
// =============== 文件系统操作 ===============
getProjectOldAndNewPaths(projectId, newName) {
const project = this.projects.find(p => p.id === projectId);
const oldPath = this.projectPaths.get(projectId);
if (!project || !oldPath)
return null;
const parentDir = path.dirname(oldPath);
// 新路径使用新的项目名称作为文件夹名
const newPath = path.join(parentDir, newName);
return { oldPath, newPath };
}
/**
* 获取飞行器目录的完整路径
*/
getAircraftDirectoryPath(aircraftId) {
const aircraft = this.getAircraft(aircraftId);
if (!aircraft)
@@ -441,10 +409,6 @@ class ProjectService {
return null;
return path.join(projectPath, aircraft.name);
}
/**
* 获取飞行器目录重命名所需的旧路径和新路径(新增)
* 注意oldPath使用内存中当前的名称newPath使用将要更新的新名称
*/
getAircraftOldAndNewPaths(aircraftId, newName) {
const aircraft = this.aircrafts.find(a => a.id === aircraftId);
if (!aircraft)
@@ -456,9 +420,6 @@ class ProjectService {
const newPath = path.join(projectPath, newName);
return { oldPath, newPath };
}
/**
* 获取容器目录的完整路径
*/
getContainerDirectoryPath(containerId) {
const container = this.getContainer(containerId);
if (!container)
@@ -471,10 +432,6 @@ class ProjectService {
return null;
return path.join(projectPath, aircraft.name, container.name);
}
/**
* 获取容器目录重命名所需的旧路径和新路径(新增)
* 注意oldPath使用内存中当前的名称newPath使用将要更新的新名称
*/
getContainerOldAndNewPaths(containerId, newName) {
const container = this.containers.find(c => c.id === containerId);
if (!container)
@@ -490,9 +447,6 @@ class ProjectService {
const newPath = path.join(aircraftDir, newName);
return { oldPath, newPath };
}
/**
* 递归删除目录
*/
async deleteDirectoryFromDisk(directoryPath) {
if (!directoryPath)
return false;
@@ -506,12 +460,9 @@ class ProjectService {
}
catch (error) {
console.error(`删除目录失败: ${error}`);
throw error; // Re-throw to be handled by ConfigPanel
throw error;
}
}
/**
* 重命名磁盘上的目录(新增)
*/
async renameDirectoryOnDisk(oldPath, newPath) {
if (!oldPath || !newPath)
return false;
@@ -526,7 +477,7 @@ class ProjectService {
}
catch (error) {
console.error(`重命名目录失败: ${error}`);
throw error; // Re-throw to be handled by ConfigPanel
throw error;
}
}
async createAircraftDirectory(aircraft) {
@@ -586,10 +537,6 @@ class ProjectService {
console.error(`确保容器目录存在失败: ${error}`);
}
}
/**
* 仅用于「新建容器」时的默认两个配置
* (导入容器时不会调用)
*/
async createDefaultConfigs(container) {
this.configs.push({
id: this.generateUniqueId('cfg', this.configs),
@@ -630,18 +577,15 @@ class ProjectService {
if (!projectId) {
return null;
}
// 先根据旧的 projectId 算出要清理的 aircraft / container / config / moduleFolder
const existingAircrafts = this.aircrafts.filter(a => a.projectId === projectId);
const aircraftIds = existingAircrafts.map(a => a.id);
const existingContainers = this.containers.filter(c => aircraftIds.includes(c.aircraftId));
const containerIds = existingContainers.map(c => c.id);
// 清理旧数据(同一个 projectId 的)
this.projects = this.projects.filter(p => p.id !== projectId);
this.aircrafts = this.aircrafts.filter(a => a.projectId !== projectId);
this.containers = this.containers.filter(c => !aircraftIds.includes(c.aircraftId));
this.configs = this.configs.filter(cfg => !containerIds.includes(cfg.containerId));
this.moduleFolders = this.moduleFolders.filter(folder => !containerIds.includes(folder.containerId));
// ⭐ 载入新数据时顺便过滤掉名字为 ".git" 的容器(避免历史数据带进来)
const cleanedContainers = data.containers.filter(c => c.name !== '.git');
this.projects.push(...data.projects);
this.aircrafts.push(...data.aircrafts);
@@ -694,7 +638,6 @@ class ProjectService {
const pathParts = folder.localPath.split('/').filter(part => part);
if (pathParts.length < 4)
return null;
// 路径的最后一部分是文件夹的名称
const folderName = pathParts[pathParts.length - 1];
return path.join(projectPath, aircraft.name, container.name, folderName);
}

File diff suppressed because one or more lines are too long