0
0

现在代码上传逻辑存在问题

This commit is contained in:
xubing
2025-12-03 16:08:14 +08:00
parent 3f512e8646
commit 9a34cd24e4
31 changed files with 3030 additions and 3063 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,292 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GitService = void 0;
const fs = __importStar(require("fs"));
const isomorphic_git_1 = __importDefault(require("isomorphic-git"));
const node_1 = __importDefault(require("isomorphic-git/http/node"));
const path = __importStar(require("path"));
const child_process_1 = require("child_process");
const util_1 = require("util");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
/**
* Git操作服务
*/
class GitService {
/**
* 获取远程分支列表
*/
static async fetchBranches(url, username, token) {
try {
const options = {
http: node_1.default,
url: url
};
if (username || token) {
options.onAuth = () => ({
username: username || '',
password: token || ''
});
}
const refs = await isomorphic_git_1.default.listServerRefs(options);
const branchRefs = refs.filter((ref) => ref.ref.startsWith('refs/heads/') || ref.ref.startsWith('refs/remotes/origin/'));
const branches = branchRefs.map((ref) => {
let branchName;
if (ref.ref.startsWith('refs/remotes/')) {
branchName = ref.ref.replace('refs/remotes/origin/', '');
}
else {
branchName = ref.ref.replace('refs/heads/', '');
}
return {
name: branchName,
isCurrent: branchName === 'main' || branchName === 'master',
selected: false
};
});
return branches;
}
catch (error) {
console.error('获取分支失败:', error);
throw error;
}
}
/**
* 克隆仓库
*/
static async cloneRepository(url, localPath, branch = 'main', onProgress, username, token) {
const parentDir = path.dirname(localPath);
await fs.promises.mkdir(parentDir, { recursive: true });
// 检查目录是否已存在且非空(不变)
let dirExists = false;
try {
await fs.promises.access(localPath);
dirExists = true;
}
catch {
dirExists = false;
}
if (dirExists) {
const dirContents = await fs.promises.readdir(localPath);
if (dirContents.length > 0 && dirContents.some(item => item !== '.git')) {
throw new Error('目标目录不为空,请清空目录或选择其他路径');
}
}
const options = {
fs,
http: node_1.default,
dir: localPath,
url,
singleBranch: true,
depth: 1,
ref: branch,
onProgress
};
if (username || token) {
options.onAuth = () => ({
username: username || '',
password: token || ''
});
}
await isomorphic_git_1.default.clone(options);
}
/**
* 拉取最新更改
*/
static async pullChanges(localPath) {
await isomorphic_git_1.default.pull({
fs: fs,
http: node_1.default,
dir: localPath,
author: { name: 'DCSP User', email: 'user@dcsp.local' },
fastForward: true
});
}
/**
* 构建分支树结构
*/
static buildBranchTree(branches) {
const root = [];
branches.forEach(branch => {
const parts = branch.name.split('/');
let currentLevel = root;
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const isLeaf = i === parts.length - 1;
const fullName = parts.slice(0, i + 1).join('/');
let node = currentLevel.find((n) => n.name === part);
if (!node) {
node = {
name: part,
fullName: fullName,
isLeaf: isLeaf,
children: [],
level: i,
expanded: true
};
currentLevel.push(node);
}
if (isLeaf) {
node.branch = branch;
}
currentLevel = node.children;
}
});
return root;
}
/**
* 初始化Git仓库
*/
static async initRepository(localPath, branchName) {
await execAsync(`git init && git checkout -b "${branchName}"`, { cwd: localPath });
}
/**
* 添加远程仓库
*/
static async addRemote(localPath, repoUrl, username, token) {
let finalUrl = repoUrl;
if (username && token) {
const u = encodeURIComponent(username);
const t = encodeURIComponent(token);
finalUrl = repoUrl.replace('://', `://${u}:${t}@`);
}
await execAsync(`git remote add origin "${finalUrl}"`, { cwd: localPath });
}
/**
* 提交初始文件
*/
static async commitInitialFiles(localPath) {
try {
await execAsync('git add .', { cwd: localPath });
await execAsync(`git commit -m "Initial commit from DCSP - ${new Date().toLocaleString()}"`, { cwd: localPath });
}
catch (error) {
if (error.stderr?.includes('nothing to commit')) {
console.log('没有需要提交的更改');
}
else {
throw error;
}
}
}
/**
* 推送到远程仓库
*/
static async pushToRemote(localPath, branchName) {
await execAsync(`git push -u origin "${branchName}" --force`, { cwd: localPath });
}
/**
* 使用Git命令提交并推送
*/
static async commitAndPush(localPath) {
await execAsync('git add .', { cwd: localPath });
await execAsync(`git commit -m "Auto commit from DCSP - ${new Date().toLocaleString()}"`, { cwd: localPath });
await execAsync('git push', { cwd: localPath });
}
/**
* 检查是否有未提交的更改
*/
static async hasUncommittedChanges(localPath) {
try {
const { stdout } = await execAsync('git status --porcelain', { cwd: localPath });
return !!stdout.trim();
}
catch {
return false;
}
}
/**
* 推送到指定仓库URL
*/
static async pushToRepoUrl(localPath, repoUrl, branchName, username, token) {
let finalUrl = repoUrl;
if (username && token) {
const u = encodeURIComponent(username);
const t = encodeURIComponent(token);
finalUrl = repoUrl.replace('://', `://${u}:${t}@`);
}
const commands = [
'git fetch --unshallow || true',
`git checkout -B "${branchName}"`,
`git push -u "${finalUrl}" "${branchName}" --force`
];
await execAsync(commands.join(' && '), { cwd: localPath });
}
/**
* 生成模块文件夹名称
*/
static generateModuleFolderName(url, branch) {
const repoName = url.split('/').pop()?.replace('.git', '') || 'unknown-repo';
const branchSafeName = branch.replace(/[^a-zA-Z0-9-_]/g, '-');
return {
displayName: repoName,
folderName: branchSafeName
};
}
/**
* 构建文件树
*/
static async buildFileTree(dir, relativePath = '') {
try {
const files = await fs.promises.readdir(dir);
const tree = [];
for (const file of files) {
if (file.startsWith('.') && file !== '.git')
continue;
if (file === '.dcsp-data.json')
continue;
const filePath = path.join(dir, file);
const stats = await fs.promises.stat(filePath);
const currentRelativePath = path.join(relativePath, file);
if (stats.isDirectory()) {
const children = await GitService.buildFileTree(filePath, currentRelativePath);
tree.push({
name: file,
type: 'folder',
path: currentRelativePath,
children: children
});
}
else {
tree.push({
name: file,
type: 'file',
path: currentRelativePath
});
}
}
return tree;
}
catch (error) {
console.error('构建文件树失败:', error);
return [];
}
}
}
exports.GitService = GitService;
//# sourceMappingURL=GitService.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,441 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectService = void 0;
// src/panels/services/ProjectService.ts
const vscode = __importStar(require("vscode"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const StorageService_1 = require("./StorageService");
/**
* 项目数据管理服务
*/
class ProjectService {
constructor() {
this.projects = [];
this.aircrafts = [];
this.containers = [];
this.configs = [];
this.moduleFolders = [];
this.projectPaths = new Map();
}
/**
* 生成唯一ID
*/
generateUniqueId(prefix, _existingItems = []) {
const randomPart = `${Date.now().toString(36)}${Math.random().toString(36).substring(2, 8)}`;
return `${prefix}${randomPart}`;
}
// =============== 项目相关方法 ===============
getProjects() {
return this.projects;
}
getProjectPaths() {
return this.projectPaths;
}
getProjectPath(projectId) {
return this.projectPaths.get(projectId);
}
setProjectPath(projectId, path) {
this.projectPaths.set(projectId, path);
}
async createProject(name) {
const newId = this.generateUniqueId('p', this.projects);
const newProject = {
id: newId,
name: name
};
this.projects.push(newProject);
return newId;
}
updateProjectName(projectId, newName) {
const project = this.projects.find(p => p.id === projectId);
if (project) {
project.name = newName;
return true;
}
return false;
}
deleteProject(projectId) {
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));
this.configs = this.configs.filter(cfg => !containerIds.includes(cfg.containerId));
this.moduleFolders = this.moduleFolders.filter(folder => !containerIds.includes(folder.containerId));
this.projectPaths.delete(projectId);
return true;
}
// =============== 飞行器相关方法 ===============
getAircraftsByProject(projectId) {
return this.aircrafts.filter(a => a.projectId === projectId);
}
async createAircraft(name, projectId) {
const newId = this.generateUniqueId('a', this.aircrafts);
const newAircraft = {
id: newId,
name: name,
projectId: projectId
};
this.aircrafts.push(newAircraft);
// 创建飞行器目录
await this.createAircraftDirectory(newAircraft);
return newId;
}
updateAircraftName(aircraftId, newName) {
const aircraft = this.aircrafts.find(a => a.id === aircraftId);
if (aircraft) {
aircraft.name = newName;
return true;
}
return false;
}
deleteAircraft(aircraftId) {
const aircraft = this.aircrafts.find(a => a.id === aircraftId);
if (!aircraft)
return false;
// ⚠️ 修正点:先在删除 containers 之前,算出要删的 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;
}
// =============== 容器相关方法 ===============
getContainersByAircraft(aircraftId) {
return this.containers.filter(c => c.aircraftId === aircraftId);
}
async createContainer(name, aircraftId) {
const newId = this.generateUniqueId('c', this.containers);
const newContainer = {
id: newId,
name: name,
aircraftId: aircraftId
};
this.containers.push(newContainer);
await this.createContainerDirectory(newContainer);
await this.createDefaultConfigs(newContainer);
return newId;
}
updateContainerName(containerId, newName) {
const container = this.containers.find(c => c.id === containerId);
if (container) {
container.name = newName;
return true;
}
return false;
}
deleteContainer(containerId) {
const container = this.containers.find(c => c.id === containerId);
if (!container)
return false;
this.containers = this.containers.filter(c => c.id !== containerId);
this.configs = this.configs.filter(cfg => cfg.containerId !== containerId);
this.moduleFolders = this.moduleFolders.filter(folder => folder.containerId !== containerId);
return true;
}
// =============== 配置相关方法 ===============
getConfigsByContainer(containerId) {
return this.configs.filter(cfg => cfg.containerId === containerId);
}
getConfig(configId) {
return this.configs.find(c => c.id === configId);
}
async createConfig(name, containerId) {
const newId = this.generateUniqueId('cfg', this.configs);
const newConfig = {
id: newId,
name: name,
fileName: name.toLowerCase().replace(/\s+/g, '_'),
content: `# ${name} 配置文件\n# 创建时间: ${new Date().toLocaleString()}\n# 您可以在此编辑配置内容\n\n`,
containerId: containerId
};
this.configs.push(newConfig);
await this.ensureContainerDirectoryExists(containerId);
return newId;
}
updateConfigName(configId, newName) {
const config = this.configs.find(c => c.id === configId);
if (config) {
config.name = newName;
return true;
}
return false;
}
deleteConfig(configId) {
const config = this.configs.find(c => c.id === configId);
if (!config)
return false;
this.configs = this.configs.filter(c => c.id !== configId);
return true;
}
// =============== 模块文件夹相关方法 ===============
getModuleFoldersByContainer(containerId) {
return this.moduleFolders.filter(folder => folder.containerId === containerId);
}
getModuleFolder(folderId) {
return this.moduleFolders.find(f => f.id === folderId);
}
addModuleFolder(folder) {
this.moduleFolders.push(folder);
}
updateModuleFolder(folderId, updates) {
const folderIndex = this.moduleFolders.findIndex(f => f.id === folderId);
if (folderIndex === -1)
return false;
this.moduleFolders[folderIndex] = {
...this.moduleFolders[folderIndex],
...updates
};
return true;
}
deleteModuleFolder(folderId) {
const folder = this.moduleFolders.find(f => f.id === folderId);
if (!folder)
return false;
this.moduleFolders = this.moduleFolders.filter(f => f.id !== folderId);
return true;
}
renameModuleFolder(folderId, newName) {
const folder = this.moduleFolders.find(f => f.id === folderId);
if (!folder)
return false;
const oldName = folder.localPath.split('/').pop() || '';
folder.localPath = folder.localPath.replace(/\/[^/]+$/, '/' + newName);
return true;
}
// =============== 文件系统操作 ===============
async createAircraftDirectory(aircraft) {
try {
const projectPath = this.projectPaths.get(aircraft.projectId);
if (!projectPath) {
console.warn('未找到项目路径,跳过创建飞行器目录');
return;
}
const aircraftDir = vscode.Uri.joinPath(vscode.Uri.file(projectPath), aircraft.name);
await vscode.workspace.fs.createDirectory(aircraftDir);
console.log(`✅ 创建飞行器目录: ${aircraftDir.fsPath}`);
}
catch (error) {
console.error(`创建飞行器目录失败: ${error}`);
}
}
async createContainerDirectory(container) {
try {
const aircraft = this.aircrafts.find(a => a.id === container.aircraftId);
if (!aircraft) {
console.warn('未找到对应的飞行器,跳过创建容器目录');
return;
}
const projectPath = this.projectPaths.get(aircraft.projectId);
if (!projectPath) {
console.warn('未找到项目路径,跳过创建容器目录');
return;
}
const aircraftDir = vscode.Uri.joinPath(vscode.Uri.file(projectPath), aircraft.name);
const containerDir = vscode.Uri.joinPath(aircraftDir, container.name);
await vscode.workspace.fs.createDirectory(aircraftDir);
await vscode.workspace.fs.createDirectory(containerDir);
console.log(`✅ 创建容器目录: ${containerDir.fsPath}`);
}
catch (error) {
console.error(`创建容器目录失败: ${error}`);
}
}
async ensureContainerDirectoryExists(containerId) {
try {
const container = this.containers.find(c => c.id === containerId);
if (!container)
return;
const aircraft = this.aircrafts.find(a => a.id === container.aircraftId);
if (!aircraft)
return;
const projectPath = this.projectPaths.get(aircraft.projectId);
if (!projectPath)
return;
const aircraftDir = vscode.Uri.joinPath(vscode.Uri.file(projectPath), aircraft.name);
const containerDir = vscode.Uri.joinPath(aircraftDir, container.name);
await vscode.workspace.fs.createDirectory(aircraftDir);
await vscode.workspace.fs.createDirectory(containerDir);
}
catch (error) {
console.error(`确保容器目录存在失败: ${error}`);
}
}
async createDefaultConfigs(container) {
this.configs.push({
id: this.generateUniqueId('cfg', this.configs),
name: '配置1',
fileName: 'dockerfile',
content: `# ${container.name} 的 Dockerfile\nFROM ubuntu:20.04\n\n# 设置工作目录\nWORKDIR /app\n\n# 复制文件\nCOPY . .\n\n# 安装依赖\nRUN apt-get update && apt-get install -y \\\n python3 \\\n python3-pip\n\n# 暴露端口\nEXPOSE 8080\n\n# 启动命令\nCMD ["python3", "app.py"]`,
containerId: container.id
});
this.configs.push({
id: this.generateUniqueId('cfg', this.configs),
name: '配置2',
fileName: 'docker-compose.yml',
content: `# ${container.name} 的 Docker Compose 配置\nversion: '3.8'\n\nservices:\n ${container.name.toLowerCase().replace(/\s+/g, '-')}:\n build: .\n container_name: ${container.name}\n ports:\n - "8080:8080"\n environment:\n - NODE_ENV=production\n volumes:\n - ./data:/app/data\n restart: unless-stopped`,
containerId: container.id
});
}
// =============== 数据持久化 ===============
async saveCurrentProjectData(projectId) {
try {
const projectPath = this.projectPaths.get(projectId);
if (!projectPath) {
console.warn('未找到项目存储路径,数据将不会保存');
return false;
}
const data = this.getProjectData(projectId);
return await StorageService_1.StorageService.saveProjectData(projectPath, data);
}
catch (error) {
console.error('保存项目数据失败:', error);
return false;
}
}
async loadProjectData(projectPath) {
try {
const data = await StorageService_1.StorageService.loadProjectData(projectPath);
if (!data) {
return null;
}
const projectId = data.projects[0]?.id;
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));
// 加载新数据
this.projects.push(...data.projects);
this.aircrafts.push(...data.aircrafts);
this.containers.push(...data.containers);
this.configs.push(...data.configs);
this.moduleFolders.push(...data.moduleFolders);
this.projectPaths.set(projectId, projectPath);
return projectId;
}
catch (error) {
console.error('加载项目数据失败:', error);
return null;
}
}
getProjectData(projectId) {
return {
projects: [this.projects.find(p => p.id === projectId)],
aircrafts: this.aircrafts.filter(a => a.projectId === projectId),
containers: this.containers.filter(c => {
const aircraft = this.aircrafts.find(a => a.id === c.aircraftId);
return aircraft && aircraft.projectId === projectId;
}),
configs: this.configs.filter(cfg => {
const container = this.containers.find(c => c.id === cfg.containerId);
if (!container)
return false;
const aircraft = this.aircrafts.find(a => a.id === container.aircraftId);
return aircraft && aircraft.projectId === projectId;
}),
moduleFolders: this.moduleFolders.filter(folder => {
const container = this.containers.find(c => c.id === folder.containerId);
if (!container)
return false;
const aircraft = this.aircrafts.find(a => a.id === container.aircraftId);
return aircraft && aircraft.projectId === projectId;
})
};
}
// =============== 工具方法 ===============
getModuleFolderFullPath(folder) {
const container = this.containers.find(c => c.id === folder.containerId);
if (!container)
return null;
const aircraft = this.aircrafts.find(a => a.id === container.aircraftId);
if (!aircraft)
return null;
const projectPath = this.projectPaths.get(aircraft.projectId);
if (!projectPath)
return null;
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);
}
getConfigFilePath(configId) {
const config = this.configs.find(c => c.id === configId);
if (!config)
return null;
const container = this.containers.find(c => c.id === config.containerId);
if (!container)
return null;
const aircraft = this.aircrafts.find(a => a.id === container.aircraftId);
if (!aircraft)
return null;
const projectPath = this.projectPaths.get(aircraft.projectId);
if (!projectPath)
return null;
return path.join(projectPath, aircraft.name, container.name, config.fileName);
}
async deleteConfigFileFromDisk(configId) {
const filePath = this.getConfigFilePath(configId);
if (!filePath)
return false;
try {
if (fs.existsSync(filePath)) {
await fs.promises.unlink(filePath);
console.log(`✅ 已删除配置文件: ${filePath}`);
return true;
}
return false;
}
catch (error) {
console.error(`删除配置文件失败: ${error}`);
return false;
}
}
}
exports.ProjectService = ProjectService;
//# sourceMappingURL=ProjectService.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,152 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StorageService = void 0;
// src/panels/services/StorageService.ts
const vscode = __importStar(require("vscode"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
/**
* 数据存储服务
* 负责项目的持久化存储和加载
*/
class StorageService {
/**
* 加载项目数据
*/
static async loadProjectData(projectPath) {
try {
const dataUri = vscode.Uri.joinPath(vscode.Uri.file(projectPath), '.dcsp-data.json');
try {
await vscode.workspace.fs.stat(dataUri);
}
catch {
console.warn('未找到项目数据文件');
return null;
}
const fileData = await vscode.workspace.fs.readFile(dataUri);
const dataStr = new TextDecoder().decode(fileData);
const data = JSON.parse(dataStr);
return data;
}
catch (error) {
console.error('加载项目数据失败:', error);
return null;
}
}
/**
* 保存项目数据
*/
static async saveProjectData(projectPath, data) {
try {
const dataUri = vscode.Uri.joinPath(vscode.Uri.file(projectPath), '.dcsp-data.json');
const uint8Array = new TextEncoder().encode(JSON.stringify(data, null, 2));
await vscode.workspace.fs.writeFile(dataUri, uint8Array);
console.log('✅ 项目数据已保存');
return true;
}
catch (error) {
console.error('保存项目数据失败:', error);
return false;
}
}
/**
* 检查项目路径是否有数据
*/
static async checkProjectPathHasData(projectPath) {
try {
const dataUri = vscode.Uri.joinPath(vscode.Uri.file(projectPath), '.dcsp-data.json');
await vscode.workspace.fs.stat(dataUri);
return true;
}
catch {
return false;
}
}
/**
* 加载仓库配置
*/
static async loadRepoConfigs(extensionUri) {
try {
const configPath = path.join(extensionUri.fsPath, 'dcsp-repos.json');
if (!fs.existsSync(configPath)) {
return [];
}
const content = await fs.promises.readFile(configPath, 'utf8');
if (!content.trim()) {
return [];
}
const parsed = JSON.parse(content);
if (Array.isArray(parsed)) {
return parsed;
}
else if (parsed && Array.isArray(parsed.repos)) {
return parsed.repos;
}
else {
return [];
}
}
catch (error) {
console.error('加载仓库配置失败:', error);
return [];
}
}
/**
* 确保仓库配置文件存在
*/
static async ensureRepoConfigFileExists(extensionUri) {
const configPath = path.join(extensionUri.fsPath, 'dcsp-repos.json');
if (!fs.existsSync(configPath)) {
const defaultContent = JSON.stringify({
repos: [
{
name: 'example-repo',
url: 'https://github.com/username/repo.git',
username: '',
token: ''
}
]
}, null, 2);
await fs.promises.writeFile(configPath, defaultContent, 'utf8');
}
}
/**
* 打开仓库配置文件
*/
static async openRepoConfig(extensionUri) {
try {
await StorageService.ensureRepoConfigFileExists(extensionUri);
const configPath = path.join(extensionUri.fsPath, 'dcsp-repos.json');
const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(configPath));
await vscode.window.showTextDocument(doc);
}
catch (error) {
vscode.window.showErrorMessage(`打开仓库配置文件失败: ${error}`);
}
}
}
exports.StorageService = StorageService;
//# sourceMappingURL=StorageService.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StorageService.js","sourceRoot":"","sources":["../../../src/panels/services/StorageService.ts"],"names":[],"mappings":"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"}

View File

@@ -6,7 +6,6 @@ const BaseView_1 = require("./BaseView");
class AircraftView extends BaseView_1.BaseView {
render(data) {
const aircrafts = data?.aircrafts || [];
console.log('AircraftView 渲染数据:', aircrafts);
const aircraftsHtml = aircrafts.map(aircraft => `
<tr>
<td>
@@ -60,7 +59,7 @@ class AircraftView extends BaseView_1.BaseView {
display: flex;
align-items: center;
gap: 6px;
margin: 0 auto; /* 确保按钮在容器内居中 */
margin: 0 auto;
}
.btn-new:hover {
background: var(--vscode-button-hoverBackground);
@@ -74,7 +73,6 @@ class AircraftView extends BaseView_1.BaseView {
.aircraft-name:hover {
background: var(--vscode-input-background);
}
/* 专门为新建按钮的容器添加样式 */
.new-button-container {
text-align: center;
padding: 20px;
@@ -97,7 +95,6 @@ class AircraftView extends BaseView_1.BaseView {
</thead>
<tbody>
${aircraftsHtml}
<!-- 修复:使用专门的容器类确保居中 -->
<tr>
<td colspan="3" class="new-button-container">
<button class="btn-new" onclick="createNewAircraft()">+ 新建飞行器</button>
@@ -186,86 +183,6 @@ class AircraftView extends BaseView_1.BaseView {
}
);
}
// 对话框函数
function showConfirmDialog(title, message, onConfirm, onCancel) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'confirmModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closeConfirmDialog(false)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closeConfirmDialog(true)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
window.confirmCallback = function(result) {
if (result && onConfirm) {
onConfirm();
} else if (!result && onCancel) {
onCancel();
}
delete window.confirmCallback;
};
}
function closeConfirmDialog(result) {
const modal = document.getElementById('confirmModal');
if (modal) {
modal.remove();
}
if (window.confirmCallback) {
window.confirmCallback(result);
}
}
function showPromptDialog(title, message, defaultValue, onConfirm) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'promptModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<input type="text" id="promptInput" value="\${defaultValue}" style="width: 100%; margin: 10px 0; padding: 6px; background: var(--vscode-input-background); color: var(--vscode-input-foreground); border: 1px solid var(--vscode-input-border);">
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closePromptDialog(null)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closePromptDialog(document.getElementById('promptInput').value)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
setTimeout(() => {
const input = document.getElementById('promptInput');
if (input) {
input.focus();
input.select();
}
}, 100);
window.promptCallback = onConfirm;
}
function closePromptDialog(result) {
const modal = document.getElementById('promptModal');
if (modal) {
modal.remove();
}
if (window.promptCallback) {
window.promptCallback(result);
}
delete window.promptCallback;
}
</script>
</body>
</html>`;

View File

@@ -1 +1 @@
{"version":3,"file":"AircraftView.js","sourceRoot":"","sources":["../../../src/panels/views/AircraftView.ts"],"names":[],"mappings":";;;AAAA,mCAAmC;AACnC,yCAAsC;AAGtC,MAAa,YAAa,SAAQ,mBAAQ;IACtC,MAAM,CAAC,IAAwC;QAC3C,MAAM,SAAS,GAAG,IAAI,EAAE,SAAS,IAAI,EAAE,CAAC;QAExC,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC;QAE7C,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;;;oEAGY,QAAQ,CAAC,EAAE,QAAQ,QAAQ,CAAC,IAAI;;;2EAGzB,QAAQ,CAAC,EAAE,OAAO,QAAQ,CAAC,SAAS;;;0EAGrC,QAAQ,CAAC,EAAE;;;SAG5E,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,OAAO;;;;;;MAMT,IAAI,CAAC,SAAS,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAsER,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA4KnB,CAAC;IACL,CAAC;CACJ;AA9QD,oCA8QC"}
{"version":3,"file":"AircraftView.js","sourceRoot":"","sources":["../../../src/panels/views/AircraftView.ts"],"names":[],"mappings":";;;AAAA,mCAAmC;AACnC,yCAAsC;AAGtC,MAAa,YAAa,SAAQ,mBAAQ;IACtC,MAAM,CAAC,IAAwC;QAC3C,MAAM,SAAS,GAAG,IAAI,EAAE,SAAS,IAAI,EAAE,CAAC;QAExC,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;;;oEAGY,QAAQ,CAAC,EAAE,QAAQ,QAAQ,CAAC,IAAI;;;2EAGzB,QAAQ,CAAC,EAAE,OAAO,QAAQ,CAAC,SAAS;;;0EAGrC,QAAQ,CAAC,EAAE;;;SAG5E,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,OAAO;;;;;;MAMT,IAAI,CAAC,SAAS,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAqER,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA2FnB,CAAC;IACL,CAAC;CACJ;AA1LD,oCA0LC"}

View File

@@ -5,7 +5,10 @@ class BaseView {
constructor(extensionUri) {
this.extensionUri = extensionUri;
}
getStyles() {
/**
* 获取通用的样式和脚本
*/
getBaseStylesAndScripts() {
return `
<style>
body {
@@ -169,6 +172,94 @@ class BaseView {
}
</style>
<script>
// 对话框工具函数
function showConfirmDialog(title, message, onConfirm, onCancel) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'confirmModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closeConfirmDialog(false)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closeConfirmDialog(true)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
window.confirmCallback = function(result) {
if (result && onConfirm) {
onConfirm();
} else if (!result && onCancel) {
onCancel();
}
delete window.confirmCallback;
};
}
function closeConfirmDialog(result) {
const modal = document.getElementById('confirmModal');
if (modal) {
modal.remove();
}
if (window.confirmCallback) {
window.confirmCallback(result);
}
}
function showPromptDialog(title, message, defaultValue, onConfirm) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'promptModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<input type="text" id="promptInput" value="\${defaultValue}" style="width: 100%; margin: 10px 0; padding: 6px; background: var(--vscode-input-background); color: var(--vscode-input-foreground); border: 1px solid var(--vscode-input-border);">
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closePromptDialog(null)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closePromptDialog(document.getElementById('promptInput').value)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
setTimeout(() => {
const input = document.getElementById('promptInput');
if (input) {
input.focus();
input.select();
}
}, 100);
window.promptCallback = onConfirm;
}
function closePromptDialog(result) {
const modal = document.getElementById('promptModal');
if (modal) {
modal.remove();
}
if (window.promptCallback) {
window.promptCallback(result);
}
delete window.promptCallback;
}
</script>
`;
}
/**
* 获取仓库选择对话框的脚本仅ConfigView需要
*/
getRepoSelectScript() {
return `
<script>
(function () {
// 避免在同一个 Webview 中重复注册
if (window.__dcspRepoDialogInitialized) {
@@ -201,7 +292,7 @@ class BaseView {
var noRepoTip = '';
if (!hasRepos) {
noRepoTip = '<div style="margin-top:8px; font-size:12px; color: var(--vscode-descriptionForeground);">当前配置中没有仓库,请先在仓库配置中添加。</div>';
noRepoTip = '<div style="margin-top:8px; font-size:12px; color: var(--vscode-descriptionForeground);">当前配置中没有仓库,请先在"仓库配置"中添加。</div>';
}
overlay.innerHTML =
@@ -228,7 +319,6 @@ class BaseView {
if (cancelBtn) {
cancelBtn.addEventListener('click', function () {
// 通知后端取消,清理可能的状态(例如待上传的本地文件夹)
if (typeof vscode !== 'undefined') {
vscode.postMessage({
type: 'repoSelectCanceled'
@@ -271,6 +361,12 @@ class BaseView {
</script>
`;
}
/**
* 获取样式(被子类覆盖)
*/
getStyles() {
return this.getBaseStylesAndScripts();
}
}
exports.BaseView = BaseView;
//# sourceMappingURL=BaseView.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"BaseView.js","sourceRoot":"","sources":["../../../src/panels/views/BaseView.ts"],"names":[],"mappings":";;;AAEA,MAAsB,QAAQ;IAG1B,YAAY,YAAwB;QAChC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;IAIS,SAAS;QACf,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAuQN,CAAC;IACN,CAAC;CACJ;AAnRD,4BAmRC"}
{"version":3,"file":"BaseView.js","sourceRoot":"","sources":["../../../src/panels/views/BaseView.ts"],"names":[],"mappings":";;;AAGA,MAAsB,QAAQ;IAG1B,YAAY,YAAwB;QAChC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;IAID;;OAEG;IACO,uBAAuB;QAC7B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAmPN,CAAC;IACN,CAAC;IAED;;OAEG;IACO,mBAAmB;QACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAqGN,CAAC;IACN,CAAC;IAED;;OAEG;IACO,SAAS;QACf,OAAO,IAAI,CAAC,uBAAuB,EAAE,CAAC;IAC1C,CAAC;CACJ;AArXD,4BAqXC"}

View File

@@ -11,7 +11,7 @@ class ConfigView extends BaseView_1.BaseView {
const moduleFolderFileTree = data?.moduleFolderFileTree || [];
const moduleFolderLoading = data?.moduleFolderLoading || false;
const gitBranches = data?.gitBranches || [];
// 生成配置列表的 HTML - 包含配置文件和模块文件夹
// 生成配置列表的 HTML
const configsHtml = configs.map((config) => `
<tr>
<td>
@@ -27,10 +27,9 @@ class ConfigView extends BaseView_1.BaseView {
</td>
</tr>
`).join('');
// 生成模块文件夹的 HTML - 按类别分类显示
// 生成模块文件夹的 HTML
const moduleFoldersHtml = moduleFolders.map((folder) => {
const icon = '📁';
// 根据类型和上传状态确定类别显示
let category = folder.type === 'git' ? 'git' : 'local';
if (folder.uploaded) {
category += '(已上传)';
@@ -39,14 +38,12 @@ class ConfigView extends BaseView_1.BaseView {
return `
<tr>
<td>
<!-- 左侧:📁 动力学仓库 / test仅用于重命名 -->
<span class="editable clickable" onclick="renameModuleFolder('${folder.id}', '${folder.name}')">
${icon} ${folder.name}
</span>
</td>
<td class="category-${folder.type}">${category}</td>
<td>
<!-- 右侧:文件/文件夹栏,只负责选择并打开文件,不再触发重命名 -->
<span class="clickable"
onclick="openTheModuleFolder('${folder.id}', '${folder.type}')">
📄 ${fileName}
@@ -59,7 +56,7 @@ class ConfigView extends BaseView_1.BaseView {
</tr>
`;
}).join('');
// 生成分支选择的 HTML - 使用树状结构(首次渲染可以为空,后续通过 message 更新)
// 生成分支选择的 HTML
const branchesHtml = gitBranches.length > 0 ? this.generateBranchesTreeHtml(gitBranches) : '';
return `<!DOCTYPE html>
<html lang="zh-CN">
@@ -67,7 +64,8 @@ class ConfigView extends BaseView_1.BaseView {
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>配置管理</title>
${this.getStyles()}
${this.getBaseStylesAndScripts()}
${this.getRepoSelectScript()}
<style>
.url-input-section {
background: var(--vscode-panel-background);
@@ -253,16 +251,6 @@ class ConfigView extends BaseView_1.BaseView {
color: var(--vscode-charts-orange);
font-weight: bold;
}
.category-git已上传 {
color: var(--vscode-gitDecoration-addedResourceForeground);
font-weight: bold;
}
.category-local已上传 {
color: var(--vscode-gitDecoration-addedResourceForeground);
font-weight: bold;
}
</style>
</head>
<body>
@@ -319,12 +307,11 @@ class ConfigView extends BaseView_1.BaseView {
<script>
const vscode = acquireVsCodeApi();
let currentConfigId = null;
let selectedBranches = new Set();
let branchTreeData = [];
let selectedConfigs = new Set();
// 只负责改名的函数
// 只负责"改名"的函数
function renameModuleFolder(folderId, oldName) {
showPromptDialog(
'重命名模块文件夹',
@@ -362,16 +349,14 @@ class ConfigView extends BaseView_1.BaseView {
// 在 VSCode 中打开配置文件
function openConfigFileInVSCode(configId) {
console.log('📄 在 VSCode 中打开配置文件:', configId);
vscode.postMessage({
type: 'openConfigFileInVSCode',
configId: configId
});
}
// 统一功能:打开模块文件夹(这里只负责打开,而不是改名)
// 统一功能:打开模块文件夹
function openTheModuleFolder(id, type) {
console.log('📂 打开模块文件夹:', { id, type });
vscode.postMessage({
type: 'openTheModuleFolder',
id: id,
@@ -400,50 +385,40 @@ class ConfigView extends BaseView_1.BaseView {
'确认删除',
'确定删除这个配置文件吗?',
function() {
console.log('🗑️ 删除配置文件:', configId);
vscode.postMessage({
type: 'deleteConfig',
configId: configId
});
},
function() {
console.log('❌ 用户取消删除配置文件');
}
);
}
// 删除模块文件夹功能
function deleteModuleFolder(folderId) {
console.log('🗑️ 尝试删除模块文件夹:', folderId);
showConfirmDialog(
'确认删除模块文件夹',
'确定删除这个模块文件夹吗?这将删除文件夹及其所有内容。',
function() {
console.log('✅ 用户确认删除模块文件夹:', folderId);
vscode.postMessage({
type: 'deleteModuleFolder',
folderId: folderId
});
},
function() {
console.log('❌ 用户取消删除模块文件夹');
}
);
}
// 上传模块文件夹功能(区分 local / git
// 上传模块文件夹功能
function uploadModuleFolder(folderId, folderType) {
console.log('📤 上传模块文件夹:', { folderId, folderType });
if (folderType === 'git') {
// git 类型:先选仓库,然后后端根据是否改名/是否同仓库判断是更新原分支还是新分支上传
vscode.postMessage({
type: 'openRepoSelectForGitUpload',
folderId: folderId
});
} else {
// local 类型:走 local → git 的初始化上传逻辑
vscode.postMessage({
type: 'openRepoSelectForUpload',
folderId: folderId
@@ -455,9 +430,8 @@ class ConfigView extends BaseView_1.BaseView {
vscode.postMessage({ type: 'goBackToContainers' });
}
// 🔁 通过弹窗获取仓库(用于克隆)
// 通过弹窗获取仓库
function openRepoSelect() {
console.log('📦 请求仓库列表以选择 Git 仓库(用于克隆分支)');
vscode.postMessage({
type: 'openRepoSelect'
});
@@ -470,7 +444,6 @@ class ConfigView extends BaseView_1.BaseView {
} else {
selectedBranches.delete(branchName);
}
console.log('选中的分支:', Array.from(selectedBranches));
}
function cloneSelectedBranches() {
@@ -479,8 +452,6 @@ class ConfigView extends BaseView_1.BaseView {
return;
}
console.log('🚀 开始克隆选中的分支:', Array.from(selectedBranches));
vscode.postMessage({
type: 'cloneBranches',
branches: Array.from(selectedBranches)
@@ -515,7 +486,6 @@ class ConfigView extends BaseView_1.BaseView {
}
updateMergeButtonState();
console.log('选中的配置文件:', Array.from(selectedConfigs));
}
function updateMergeButtonState() {
@@ -530,8 +500,6 @@ class ConfigView extends BaseView_1.BaseView {
alert('请至少选择两个配置文件进行合并');
return;
}
console.log('🔄 开始合并选中的配置文件:', Array.from(selectedConfigs));
showMergeDialog();
}
@@ -723,94 +691,11 @@ class ConfigView extends BaseView_1.BaseView {
return html;
}
// 对话框函数
function showConfirmDialog(title, message, onConfirm, onCancel) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'confirmModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closeConfirmDialog(false)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closeConfirmDialog(true)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
window.confirmCallback = function(result) {
if (result && onConfirm) {
onConfirm();
} else if (!result && onCancel) {
onCancel();
}
delete window.confirmCallback;
};
}
function closeConfirmDialog(result) {
const modal = document.getElementById('confirmModal');
if (modal) {
modal.remove();
}
if (window.confirmCallback) {
window.confirmCallback(result);
}
}
function showPromptDialog(title, message, defaultValue, onConfirm) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'promptModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<input type="text" id="promptInput" value="\${defaultValue}" style="width: 100%; margin: 10px 0; padding: 6px; background: var(--vscode-input-background); color: var(--vscode-input-foreground); border: 1px solid var(--vscode-input-border);">
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closePromptDialog(null)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closePromptDialog(document.getElementById('promptInput').value)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
setTimeout(() => {
const input = document.getElementById('promptInput');
if (input) {
input.focus();
input.select();
}
}, 100);
window.promptCallback = onConfirm;
}
function closePromptDialog(result) {
const modal = document.getElementById('promptModal');
if (modal) {
modal.remove();
}
if (window.promptCallback) {
window.promptCallback(result);
}
delete window.promptCallback;
}
// 消息监听
window.addEventListener('message', event => {
const message = event.data;
console.log('📨 前端收到后端消息:', message);
if (message.type === 'branchesFetched') {
console.log('🌿 收到分支数据:', message.branches);
console.log('🌳 收到分支树数据:', message.branchTree);
branchTreeData = message.branchTree || [];
renderBranchTree(branchTreeData);
}
@@ -818,8 +703,6 @@ class ConfigView extends BaseView_1.BaseView {
// 初始化
document.addEventListener('DOMContentLoaded', function() {
console.log('📄 ConfigView 页面加载完成');
document.addEventListener('change', function(event) {
if (event.target.classList.contains('config-checkbox')) {
const configId = event.target.getAttribute('data-id');
@@ -912,18 +795,6 @@ class ConfigView extends BaseView_1.BaseView {
});
return html;
}
countLeafNodes(nodes) {
let count = 0;
nodes.forEach(node => {
if (node.isLeaf) {
count++;
}
else {
count += this.countLeafNodes(node.children);
}
});
return count;
}
}
exports.ConfigView = ConfigView;
//# sourceMappingURL=ConfigView.js.map

File diff suppressed because one or more lines are too long

View File

@@ -6,9 +6,8 @@ const BaseView_1 = require("./BaseView");
class ContainerView extends BaseView_1.BaseView {
render(data) {
const project = data?.project;
const aircraft = data?.aircraft; // 新增:获取飞行器数据
const aircraft = data?.aircraft;
const containers = data?.containers || [];
// 生成容器列表的 HTML
const containersHtml = containers.map((container) => `
<tr>
<td>
@@ -116,86 +115,6 @@ class ContainerView extends BaseView_1.BaseView {
}
);
}
// 对话框函数(与之前相同)
function showConfirmDialog(title, message, onConfirm, onCancel) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'confirmModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closeConfirmDialog(false)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closeConfirmDialog(true)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
window.confirmCallback = function(result) {
if (result && onConfirm) {
onConfirm();
} else if (!result && onCancel) {
onCancel();
}
delete window.confirmCallback;
};
}
function closeConfirmDialog(result) {
const modal = document.getElementById('confirmModal');
if (modal) {
modal.remove();
}
if (window.confirmCallback) {
window.confirmCallback(result);
}
}
function showPromptDialog(title, message, defaultValue, onConfirm) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'promptModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<input type="text" id="promptInput" value="\${defaultValue}" style="width: 100%; margin: 10px 0; padding: 6px; background: var(--vscode-input-background); color: var(--vscode-input-foreground); border: 1px solid var(--vscode-input-border);">
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closePromptDialog(null)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closePromptDialog(document.getElementById('promptInput').value)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
setTimeout(() => {
const input = document.getElementById('promptInput');
if (input) {
input.focus();
input.select();
}
}, 100);
window.promptCallback = onConfirm;
}
function closePromptDialog(result) {
const modal = document.getElementById('promptModal');
if (modal) {
modal.remove();
}
if (window.promptCallback) {
window.promptCallback(result);
}
delete window.promptCallback;
}
</script>
</body>
</html>`;

View File

@@ -1 +1 @@
{"version":3,"file":"ContainerView.js","sourceRoot":"","sources":["../../../src/panels/views/ContainerView.ts"],"names":[],"mappings":";;;AAAA,oCAAoC;AACpC,yCAAsC;AAGtC,MAAa,aAAc,SAAQ,mBAAQ;IACvC,MAAM,CAAC,IAAyB;QAC5B,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,CAAC,CAAC,aAAa;QAC9C,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC;QAE1C,eAAe;QACf,MAAM,cAAc,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAA4B,EAAE,EAAE,CAAC;;;yEAGP,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC,IAAI,UAAU,SAAS,CAAC,IAAI;;;4EAGtD,SAAS,CAAC,EAAE;;;2EAGb,SAAS,CAAC,EAAE;;;SAG9E,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,OAAO;;;;;;MAMT,IAAI,CAAC,SAAS,EAAE;;;;gFAI0D,QAAQ,EAAE,IAAI,IAAI,OAAO;;;;;;;;;;;;;cAa3F,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAyJpB,CAAC;IACL,CAAC;CACJ;AAvMD,sCAuMC"}
{"version":3,"file":"ContainerView.js","sourceRoot":"","sources":["../../../src/panels/views/ContainerView.ts"],"names":[],"mappings":";;;AAAA,oCAAoC;AACpC,yCAAsC;AAGtC,MAAa,aAAc,SAAQ,mBAAQ;IACvC,MAAM,CAAC,IAAyB;QAC5B,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,CAAC;QAC9B,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,CAAC;QAChC,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC;QAE1C,MAAM,cAAc,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAA4B,EAAE,EAAE,CAAC;;;yEAGP,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC,IAAI,UAAU,SAAS,CAAC,IAAI;;;4EAGtD,SAAS,CAAC,EAAE;;;2EAGb,SAAS,CAAC,EAAE;;;SAG9E,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,OAAO;;;;;;MAMT,IAAI,CAAC,SAAS,EAAE;;;;gFAI0D,QAAQ,EAAE,IAAI,IAAI,OAAO;;;;;;;;;;;;;cAa3F,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAyEpB,CAAC;IACL,CAAC;CACJ;AAtHD,sCAsHC"}

View File

@@ -1,6 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectView = void 0;
// src/panels/views/ProjectView.ts
const BaseView_1 = require("./BaseView");
class ProjectView extends BaseView_1.BaseView {
render(data) {
@@ -135,13 +136,11 @@ class ProjectView extends BaseView_1.BaseView {
function configureProject(projectId, projectName, isConfigured) {
if (isConfigured) {
// 已配置的项目直接打开
vscode.postMessage({
type: 'openProject',
projectId: projectId
});
} else {
// 未配置的项目需要设置路径
vscode.postMessage({
type: 'configureProject',
projectId: projectId,
@@ -188,7 +187,7 @@ class ProjectView extends BaseView_1.BaseView {
);
}
// 项目名称编辑功能 - 修复版本
// 项目名称编辑功能
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('click', function(event) {
if (event.target.classList.contains('project-name')) {
@@ -219,86 +218,6 @@ class ProjectView extends BaseView_1.BaseView {
}
);
}
// 对话框函数 - 只保留一份
function showConfirmDialog(title, message, onConfirm, onCancel) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'confirmModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closeConfirmDialog(false)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closeConfirmDialog(true)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
window.confirmCallback = function(result) {
if (result && onConfirm) {
onConfirm();
} else if (!result && onCancel) {
onCancel();
}
delete window.confirmCallback;
};
}
function closeConfirmDialog(result) {
const modal = document.getElementById('confirmModal');
if (modal) {
modal.remove();
}
if (window.confirmCallback) {
window.confirmCallback(result);
}
}
function showPromptDialog(title, message, defaultValue, onConfirm) {
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.id = 'promptModal';
overlay.innerHTML = \`
<div class="modal-dialog">
<div class="modal-title">\${title}</div>
<div>\${message}</div>
<input type="text" id="promptInput" value="\${defaultValue}" style="width: 100%; margin: 10px 0; padding: 6px; background: var(--vscode-input-background); color: var(--vscode-input-foreground); border: 1px solid var(--vscode-input-border);">
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closePromptDialog(null)">取消</button>
<button class="modal-btn modal-btn-primary" onclick="closePromptDialog(document.getElementById('promptInput').value)">确定</button>
</div>
</div>
\`;
document.body.appendChild(overlay);
setTimeout(() => {
const input = document.getElementById('promptInput');
if (input) {
input.focus();
input.select();
}
}, 100);
window.promptCallback = onConfirm;
}
function closePromptDialog(result) {
const modal = document.getElementById('promptModal');
if (modal) {
modal.remove();
}
if (window.promptCallback) {
window.promptCallback(result);
}
delete window.promptCallback;
}
</script>
</body>
</html>`;

View File

@@ -1 +1 @@
{"version":3,"file":"ProjectView.js","sourceRoot":"","sources":["../../../src/panels/views/ProjectView.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AAGtC,MAAa,WAAY,SAAQ,mBAAQ;IACrC,MAAM,CAAC,IAA0E;QAC7E,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC;QACtC,MAAM,YAAY,GAAG,IAAI,EAAE,YAAY,IAAI,IAAI,GAAG,EAAE,CAAC;QAErD,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAwB,EAAE,EAAE;YAC3D,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAClD,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7C,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;YAEhD,OAAO;;;kEAG+C,OAAO,CAAC,EAAE,KAAK,UAAU,IAAI,OAAO,CAAC,IAAI;;0BAEjF,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,MAAM,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;;;;yEAItB,OAAO,CAAC,EAAE,OAAO,OAAO,CAAC,IAAI,MAAM,YAAY;0BAC9F,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;;;;yEAIqB,OAAO,CAAC,EAAE;;;SAG1E,CAAA;QAAA,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEb,OAAO;;;;;;MAMT,IAAI,CAAC,SAAS,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyER,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiMlB,CAAC;IACL,CAAC;CACJ;AA/SD,kCA+SC"}
{"version":3,"file":"ProjectView.js","sourceRoot":"","sources":["../../../src/panels/views/ProjectView.ts"],"names":[],"mappings":";;;AAAA,kCAAkC;AAClC,yCAAsC;AAGtC,MAAa,WAAY,SAAQ,mBAAQ;IACrC,MAAM,CAAC,IAA0E;QAC7E,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC;QACtC,MAAM,YAAY,GAAG,IAAI,EAAE,YAAY,IAAI,IAAI,GAAG,EAAE,CAAC;QAErD,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAwB,EAAE,EAAE;YAC3D,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAClD,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7C,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;YAEhD,OAAO;;;kEAG+C,OAAO,CAAC,EAAE,KAAK,UAAU,IAAI,OAAO,CAAC,IAAI;;0BAEjF,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,MAAM,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;;;;yEAItB,OAAO,CAAC,EAAE,OAAO,OAAO,CAAC,IAAI,MAAM,YAAY;0BAC9F,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;;;;yEAIqB,OAAO,CAAC,EAAE;;;SAG1E,CAAA;QAAA,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEb,OAAO;;;;;;MAMT,IAAI,CAAC,SAAS,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyER,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA+GlB,CAAC;IACL,CAAC;CACJ;AA7ND,kCA6NC"}