0
0
Files
vs-p/out/panels/ConfigPanel.js
2025-11-21 16:07:48 +08:00

689 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigPanel = void 0;
// src/panels/ConfigPanel.ts
const vscode = require("vscode");
const ProjectView_1 = require("./views/ProjectView");
const AircraftView_1 = require("./views/AircraftView");
const ContainerView_1 = require("./views/ContainerView");
const ConfigView_1 = require("./views/ConfigView");
class ConfigPanel {
static createOrShow(extensionUri) {
const column = vscode.window.activeTextEditor?.viewColumn || vscode.ViewColumn.One;
if (ConfigPanel.currentPanel) {
ConfigPanel.currentPanel.panel.reveal(column);
return;
}
const panel = vscode.window.createWebviewPanel('DCSP', '数字卫星构建平台', column, {
enableScripts: true,
localResourceRoots: [extensionUri],
retainContextWhenHidden: true
});
ConfigPanel.currentPanel = new ConfigPanel(panel, extensionUri);
}
constructor(panel, extensionUri) {
this.currentView = 'projects';
this.currentProjectId = '';
this.currentAircraftId = '';
this.currentContainerId = '';
// 数据存储
this.projects = [];
this.aircrafts = [];
this.containers = [];
this.configs = [];
// 项目存储路径映射
this.projectPaths = new Map();
this.panel = panel;
this.extensionUri = extensionUri;
// 初始化各个视图
this.projectView = new ProjectView_1.ProjectView(extensionUri);
this.aircraftView = new AircraftView_1.AircraftView(extensionUri);
this.containerView = new ContainerView_1.ContainerView(extensionUri);
this.configView = new ConfigView_1.ConfigView(extensionUri);
this.updateWebview();
this.setupMessageListener();
this.panel.onDidDispose(() => {
ConfigPanel.currentPanel = undefined;
});
}
setupMessageListener() {
this.panel.webview.onDidReceiveMessage(async (data) => {
switch (data.type) {
case 'openExistingProject':
await this.openExistingProject();
break;
case 'configureProject':
const selectedPath = await this.selectProjectPath(data.projectId, data.projectName);
if (selectedPath) {
this.currentView = 'aircrafts';
this.currentProjectId = data.projectId;
this.updateWebview();
}
break;
case 'openProject':
// 已配置的项目直接打开
this.currentView = 'aircrafts';
this.currentProjectId = data.projectId;
this.updateWebview();
break;
case 'openAircraftConfig':
this.currentView = 'containers';
this.currentProjectId = data.projectId;
this.currentAircraftId = data.aircraftId;
this.updateWebview();
break;
case 'openContainerConfig':
this.currentView = 'configs';
this.currentContainerId = data.containerId;
this.updateWebview();
break;
// 修复返回按钮的消息处理
case 'goBackToProjects':
this.currentView = 'projects';
// 清空当前选择的ID
this.currentProjectId = '';
this.currentAircraftId = '';
this.currentContainerId = '';
this.updateWebview();
break;
case 'goBackToAircrafts':
this.currentView = 'aircrafts';
// 保持 currentProjectId清空其他ID
this.currentAircraftId = '';
this.currentContainerId = '';
this.updateWebview();
break;
case 'goBackToContainers':
this.currentView = 'containers';
// 保持 currentProjectId 和 currentAircraftId清空 currentContainerId
this.currentContainerId = '';
this.updateWebview();
break;
case 'updateProjectName':
await this.updateProjectName(data.projectId, data.name);
break;
case 'createProject':
await this.createProject(data.name);
break;
case 'updateAircraftName':
await this.updateAircraftName(data.aircraftId, data.name);
break;
case 'createAircraft':
await this.createAircraft(data.name);
break;
case 'updateContainerName':
await this.updateContainerName(data.containerId, data.name);
break;
case 'createContainer':
await this.createContainer(data.name);
break;
case 'updateConfigName':
await this.updateConfigName(data.configId, data.name);
break;
case 'updateConfigFileName':
await this.updateConfigFileName(data.configId, data.fileName);
break;
case 'createConfig':
await this.createConfig(data.name);
break;
case 'saveConfigFile':
await this.saveConfigFileToDisk(data.configId, data.content);
break;
case 'loadConfigFile':
this.loadConfigFile(data.configId);
break;
case 'deleteProject':
await this.deleteProject(data.projectId);
break;
case 'deleteAircraft':
await this.deleteAircraft(data.aircraftId);
break;
case 'deleteContainer':
await this.deleteContainer(data.containerId);
break;
case 'deleteConfig':
await this.deleteConfig(data.configId);
break;
}
});
}
// === 打开现有项目功能 ===
async openExistingProject() {
try {
const result = await vscode.window.showOpenDialog({
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
openLabel: '选择项目文件夹',
title: '选择包含项目数据的文件夹'
});
if (result && result.length > 0) {
const selectedPath = result[0].fsPath;
await this.loadProjectData(selectedPath);
}
}
catch (error) {
vscode.window.showErrorMessage(`打开项目时出错: ${error}`);
}
}
// === 数据持久化方法 ===
/**
* 保存当前项目数据到项目路径
*/
async saveCurrentProjectData() {
try {
console.log('开始保存当前项目数据...');
console.log('当前项目ID:', this.currentProjectId);
if (!this.currentProjectId) {
console.log('未找到当前项目ID跳过保存数据');
vscode.window.showWarningMessage('未找到当前项目,数据将不会保存');
return;
}
const projectPath = this.projectPaths.get(this.currentProjectId);
if (!projectPath) {
console.log('未找到项目路径,跳过保存数据');
vscode.window.showWarningMessage('未找到项目存储路径,数据将不会保存');
return;
}
const dataUri = vscode.Uri.joinPath(vscode.Uri.file(projectPath), '.dcsp-data.json');
// 只保存与当前项目相关的数据
const currentProjectAircrafts = this.aircrafts.filter(a => a.projectId === this.currentProjectId);
const currentAircraftIds = currentProjectAircrafts.map(a => a.id);
const currentProjectContainers = this.containers.filter(c => currentAircraftIds.includes(c.aircraftId));
const currentContainerIds = currentProjectContainers.map(c => c.id);
const currentProjectConfigs = this.configs.filter(cfg => currentContainerIds.includes(cfg.containerId));
const data = {
projects: this.projects.filter(p => p.id === this.currentProjectId),
aircrafts: currentProjectAircrafts,
containers: currentProjectContainers,
configs: currentProjectConfigs
};
console.log('要保存的当前项目数据:', {
projects: data.projects.length,
aircrafts: data.aircrafts.length,
containers: data.containers.length,
configs: data.configs.length
});
const uint8Array = new TextEncoder().encode(JSON.stringify(data, null, 2));
await vscode.workspace.fs.writeFile(dataUri, uint8Array);
console.log('当前项目数据已保存到:', dataUri.fsPath);
}
catch (error) {
console.error('保存当前项目数据时出错:', error);
vscode.window.showErrorMessage(`保存项目数据失败: ${error}`);
}
}
/**
* 从项目路径加载数据
*/
async loadProjectData(projectPath) {
try {
const dataUri = vscode.Uri.joinPath(vscode.Uri.file(projectPath), '.dcsp-data.json');
// 检查数据文件是否存在
try {
await vscode.workspace.fs.stat(dataUri);
}
catch {
vscode.window.showErrorMessage('选择的文件夹中没有找到项目数据文件 (.dcsp-data.json)');
return false;
}
// 读取数据文件
const fileData = await vscode.workspace.fs.readFile(dataUri);
const dataStr = new TextDecoder().decode(fileData);
const data = JSON.parse(dataStr);
console.log('从文件加载的数据:', data);
// 清空现有数据
this.projects = [];
this.aircrafts = [];
this.containers = [];
this.configs = [];
// 验证数据格式并加载
if (data.projects && Array.isArray(data.projects)) {
this.projects = data.projects;
}
if (data.aircrafts && Array.isArray(data.aircrafts)) {
this.aircrafts = data.aircrafts;
}
if (data.containers && Array.isArray(data.containers)) {
this.containers = data.containers;
}
if (data.configs && Array.isArray(data.configs)) {
this.configs = data.configs;
}
console.log('加载后的数据状态:', {
projects: this.projects.length,
aircrafts: this.aircrafts.length,
containers: this.containers.length,
configs: this.configs.length
});
// 设置当前项目为第一个项目(如果有的话)
if (this.projects.length > 0) {
this.currentProjectId = this.projects[0].id;
this.projectPaths.set(this.currentProjectId, projectPath);
this.currentView = 'aircrafts';
}
vscode.window.showInformationMessage(`项目数据已从 ${projectPath} 加载`);
this.updateWebview();
return true;
}
catch (error) {
console.error('加载项目数据时出错:', error);
vscode.window.showErrorMessage(`加载项目数据失败: ${error}`);
return false;
}
}
/**
* 检查项目路径是否已存在数据
*/
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;
}
}
// === 项目路径选择 ===
async selectProjectPath(projectId, projectName) {
try {
// 选择现有路径或输入新路径
const choice = await vscode.window.showQuickPick([
{
label: '$(folder) 选择现有文件夹',
description: '从文件系统中选择已存在的文件夹',
value: 'select'
},
{
label: '$(new-folder) 创建新文件夹',
description: '输入新文件夹路径(将自动创建)',
value: 'create'
}
], {
placeHolder: '选择项目存储方式'
});
if (!choice) {
return null;
}
if (choice.value === 'select') {
// 选择现有路径
const result = await vscode.window.showOpenDialog({
canSelectFiles: false,
canSelectFolders: true,
canSelectMany: false,
openLabel: `选择 ${projectName} 的存储位置`,
title: `为项目 "${projectName}" 选择存储文件夹`
});
if (result && result.length > 0) {
const selectedPath = result[0].fsPath;
// 检查是否已存在数据
const hasExistingData = await this.checkProjectPathHasData(selectedPath);
if (hasExistingData) {
const loadChoice = await vscode.window.showWarningMessage(`在路径 ${selectedPath} 中检测到现有项目数据,是否加载?`, { modal: true }, '是,加载现有数据', '否,创建新项目');
if (loadChoice === '是,加载现有数据') {
// 加载现有数据
const success = await this.loadProjectData(selectedPath);
if (success) {
this.projectPaths.set(projectId, selectedPath);
this.currentView = 'aircrafts';
this.currentProjectId = projectId;
this.updateWebview();
vscode.window.showInformationMessage(`项目数据已从 ${selectedPath} 加载`);
return selectedPath;
}
}
// 如果选择不加载或加载失败,继续创建新项目
}
this.projectPaths.set(projectId, selectedPath);
vscode.window.showInformationMessage(`项目存储位置已设置: ${selectedPath}`);
// 保存初始数据
await this.saveCurrentProjectData();
return selectedPath;
}
}
else {
// 创建新路径
const pathInput = await vscode.window.showInputBox({
prompt: '请输入项目存储路径(绝对路径)',
placeHolder: `/path/to/your/project/${projectName}`,
validateInput: (value) => {
if (!value) {
return '路径不能为空';
}
return null;
}
});
if (pathInput) {
try {
// 尝试创建目录
const dirUri = vscode.Uri.file(pathInput);
await vscode.workspace.fs.createDirectory(dirUri);
this.projectPaths.set(projectId, pathInput);
vscode.window.showInformationMessage(`项目存储位置已创建: ${pathInput}`);
// 保存初始数据
await this.saveCurrentProjectData();
return pathInput;
}
catch (error) {
vscode.window.showErrorMessage(`创建目录失败: ${error}`);
return null;
}
}
}
return null;
}
catch (error) {
vscode.window.showErrorMessage(`选择存储路径时出错: ${error}`);
return null;
}
}
// 更新项目名
async updateProjectName(projectId, newName) {
const project = this.projects.find(p => p.id === projectId);
if (project) {
project.name = newName;
vscode.window.showInformationMessage(`项目名称更新: ${newName}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
}
// 创建新项目
async createProject(name) {
const newId = 'p' + (this.projects.length + 1);
const newProject = {
id: newId,
name: name
};
this.projects.push(newProject);
vscode.window.showInformationMessage(`新建项目: ${name}`);
this.updateWebview();
}
// 删除项目
async deleteProject(projectId) {
const project = this.projects.find(p => p.id === projectId);
if (!project)
return;
this.projects = this.projects.filter(p => p.id !== projectId);
// 删除相关的飞行器
const relatedAircrafts = this.aircrafts.filter(a => a.projectId === projectId);
const aircraftIds = relatedAircrafts.map(a => a.id);
this.aircrafts = this.aircrafts.filter(a => a.projectId !== projectId);
// 删除相关的容器
this.containers = this.containers.filter(c => !aircraftIds.includes(c.aircraftId));
// 删除相关的配置
const containerIds = this.containers.filter(c => aircraftIds.includes(c.aircraftId)).map(c => c.id);
this.configs = this.configs.filter(cfg => !containerIds.includes(cfg.containerId));
// 删除项目路径映射
this.projectPaths.delete(projectId);
vscode.window.showInformationMessage(`删除项目: ${project.name}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
// 更新飞行器名
async updateAircraftName(aircraftId, newName) {
const aircraft = this.aircrafts.find(a => a.id === aircraftId);
if (aircraft) {
aircraft.name = newName;
vscode.window.showInformationMessage(`飞行器名称更新: ${newName}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
}
// 创建新飞行器
async createAircraft(name) {
if (!this.currentProjectId) {
vscode.window.showErrorMessage('无法创建飞行器:未找到当前项目');
return;
}
const newId = 'a' + (this.aircrafts.length + 1);
const newAircraft = {
id: newId,
name: name,
projectId: this.currentProjectId
};
this.aircrafts.push(newAircraft);
vscode.window.showInformationMessage(`新建飞行器: ${name}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
// 删除飞行器
async deleteAircraft(aircraftId) {
const aircraft = this.aircrafts.find(a => a.id === aircraftId);
if (!aircraft)
return;
this.aircrafts = this.aircrafts.filter(a => a.id !== aircraftId);
// 删除相关的容器
this.containers = this.containers.filter(c => c.aircraftId !== aircraftId);
// 删除相关的配置
const containerIds = this.containers.filter(c => c.aircraftId === aircraftId).map(c => c.id);
this.configs = this.configs.filter(cfg => !containerIds.includes(cfg.containerId));
vscode.window.showInformationMessage(`删除飞行器: ${aircraft.name}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
// 更新容器名
async updateContainerName(containerId, newName) {
const container = this.containers.find(c => c.id === containerId);
if (container) {
container.name = newName;
vscode.window.showInformationMessage(`容器名称更新: ${newName}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
}
// 创建新容器
async createContainer(name) {
console.log('创建容器当前飞行器ID:', this.currentAircraftId);
if (!this.currentAircraftId) {
vscode.window.showErrorMessage('无法创建容器:未找到当前飞行器');
return;
}
const newId = 'c' + (this.containers.length + 1);
const newContainer = {
id: newId,
name: name,
aircraftId: this.currentAircraftId
};
this.containers.push(newContainer);
// 创建两个默认配置文件
const configCount = this.configs.length;
// 第一个配置文件
this.configs.push({
id: 'cfg' + (configCount + 1),
name: '配置1',
fileName: 'dockerfile',
content: `# ${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: newId
});
// 第二个配置文件
this.configs.push({
id: 'cfg' + (configCount + 2),
name: '配置2',
fileName: 'docker-compose.yml',
content: `# ${name} 的 Docker Compose 配置\nversion: '3.8'\n\nservices:\n ${name.toLowerCase().replace(/\\s+/g, '-')}:\n build: .\n container_name: ${name}\n ports:\n - "8080:8080"\n environment:\n - NODE_ENV=production\n volumes:\n - ./data:/app/data\n restart: unless-stopped`,
containerId: newId
});
console.log('创建容器后的数据状态:', {
containers: this.containers.length,
configs: this.configs.length
});
vscode.window.showInformationMessage(`新建容器: ${name} (包含2个默认配置文件)`);
await this.saveCurrentProjectData();
this.updateWebview();
}
// 删除容器
async deleteContainer(containerId) {
const container = this.containers.find(c => c.id === containerId);
if (!container)
return;
// 删除容器
this.containers = this.containers.filter(c => c.id !== containerId);
// 删除相关的配置
this.configs = this.configs.filter(cfg => cfg.containerId !== containerId);
vscode.window.showInformationMessage(`删除容器: ${container.name}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
// 更新配置名
async updateConfigName(configId, newName) {
const config = this.configs.find(c => c.id === configId);
if (config) {
config.name = newName;
vscode.window.showInformationMessage(`配置名称更新: ${newName}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
}
// 更新文件名
async updateConfigFileName(configId, fileName) {
const config = this.configs.find(c => c.id === configId);
if (config) {
config.fileName = fileName;
vscode.window.showInformationMessage(`文件名更新: ${fileName}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
}
// 创建新配置文件
async createConfig(name) {
const newId = 'cfg' + (this.configs.length + 1);
const newConfig = {
id: newId,
name: name,
fileName: name.toLowerCase().replace(/\s+/g, '_'),
content: `# ${name} 配置文件\n# 创建时间: ${new Date().toLocaleString()}\n# 您可以在此编辑配置内容\n\n`,
containerId: this.currentContainerId
};
this.configs.push(newConfig);
vscode.window.showInformationMessage(`新建配置: ${name}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
// 删除配置文件
async deleteConfig(configId) {
const config = this.configs.find(c => c.id === configId);
if (config) {
this.configs = this.configs.filter(c => c.id !== configId);
vscode.window.showInformationMessage(`删除配置: ${config.name}`);
await this.saveCurrentProjectData();
this.updateWebview();
}
}
// 保存配置文件到磁盘
async saveConfigFileToDisk(configId, content) {
try {
const config = this.configs.find(c => c.id === configId);
if (!config) {
vscode.window.showErrorMessage('未找到配置文件');
return;
}
const container = this.containers.find(c => c.id === config.containerId);
if (!container) {
vscode.window.showErrorMessage('未找到容器');
return;
}
const aircraft = this.aircrafts.find(a => a.id === container.aircraftId);
if (!aircraft) {
vscode.window.showErrorMessage('未找到飞行器');
return;
}
const project = this.projects.find(p => p.id === aircraft.projectId);
if (!project) {
vscode.window.showErrorMessage('未找到项目');
return;
}
const projectPath = this.projectPaths.get(aircraft.projectId);
if (!projectPath) {
vscode.window.showErrorMessage('未设置项目存储路径,请先配置项目');
return;
}
// 构建文件路径:项目路径/飞行器名/容器名/文件名
const aircraftDir = vscode.Uri.joinPath(vscode.Uri.file(projectPath), aircraft.name);
const containerDir = vscode.Uri.joinPath(aircraftDir, container.name);
const fileUri = vscode.Uri.joinPath(containerDir, config.fileName);
// 确保飞行器目录存在
try {
await vscode.workspace.fs.createDirectory(aircraftDir);
}
catch (error) {
// 目录可能已存在,忽略错误
}
// 确保容器目录存在
try {
await vscode.workspace.fs.createDirectory(containerDir);
}
catch (error) {
// 目录可能已存在,忽略错误
}
// 写入文件
const uint8Array = new TextEncoder().encode(content);
await vscode.workspace.fs.writeFile(fileUri, uint8Array);
// 更新配置内容
config.content = content;
vscode.window.showInformationMessage(`配置文件已保存: ${fileUri.fsPath}`);
// 保存数据到JSON文件
await this.saveCurrentProjectData();
}
catch (error) {
vscode.window.showErrorMessage(`保存文件时出错: ${error}`);
}
}
// 加载配置文件
loadConfigFile(configId) {
const config = this.configs.find(c => c.id === configId);
if (config) {
this.panel.webview.postMessage({
type: 'configFileLoaded',
content: config.content || `# ${config.name} 的配置文件\n# 您可以在此编辑配置内容\n\n`
});
}
else {
this.panel.webview.postMessage({
type: 'configFileLoaded',
content: `# 这是 ${configId} 的配置文件\n# 您可以在此编辑配置内容\n\napp.name = "示例应用"\napp.port = 8080\napp.debug = true`
});
}
}
// 更新视图
updateWebview() {
this.panel.webview.html = this.getWebviewContent();
}
getWebviewContent() {
switch (this.currentView) {
case 'projects':
return this.projectView.render({
projects: this.projects,
projectPaths: this.projectPaths
});
case 'aircrafts':
const projectAircrafts = this.aircrafts.filter(a => a.projectId === this.currentProjectId);
return this.aircraftView.render({
aircrafts: projectAircrafts
});
case 'containers':
const currentProject = this.projects.find(p => p.id === this.currentProjectId);
const currentAircraft = this.aircrafts.find(a => a.id === this.currentAircraftId);
const projectContainers = this.containers.filter(c => c.aircraftId === this.currentAircraftId);
return this.containerView.render({
project: currentProject,
aircraft: currentAircraft,
containers: projectContainers
});
case 'configs':
const currentContainer = this.containers.find(c => c.id === this.currentContainerId);
const containerConfigs = this.configs.filter(cfg => cfg.containerId === this.currentContainerId);
return this.configView.render({
container: currentContainer,
configs: containerConfigs
});
default:
return this.projectView.render({
projects: this.projects,
projectPaths: this.projectPaths
});
}
}
}
exports.ConfigPanel = ConfigPanel;
//# sourceMappingURL=ConfigPanel.js.map