0
0

初步给项目、飞行器、容器页面添加了git拉取功能

This commit is contained in:
xubing
2025-12-05 20:17:05 +08:00
parent a7edfb82c5
commit 2c314b9b0b
19 changed files with 2557 additions and 156 deletions

View File

@@ -24,7 +24,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigPanel = void 0;
// src/panels/ConfigPanel.ts
const vscode = __importStar(require("vscode"));
const path = __importStar(require("path"));
const ProjectView_1 = require("./views/ProjectView");
@@ -60,6 +59,7 @@ class ConfigPanel {
this.currentModuleFolderId = '';
// 仓库配置
this.repoConfigs = [];
this.currentCloneScope = 'config';
// 状态管理
this.isWebviewDisposed = false;
this.currentModuleFolderFileTree = [];
@@ -93,9 +93,10 @@ class ConfigPanel {
await this.loadRepoConfigs();
}
/**
* 弹出仓库选择弹窗(仅用于“获取仓库 -> 获取分支”)
* 按作用域弹出仓库选择弹窗(仅用于“获取仓库 -> 获取分支”)
*/
async openRepoSelect() {
async openRepoSelectForScope(scope) {
this.currentCloneScope = scope;
await this.loadRepoConfigs();
if (this.repoConfigs.length === 0) {
vscode.window.showWarningMessage('尚未配置任何仓库,请先点击右上角 "仓库配置" 按钮编辑 dcsp-repos.json。');
@@ -108,6 +109,12 @@ class ConfigPanel {
repos: this.repoConfigs.map(r => ({ name: r.name }))
});
}
/**
* ConfigView 使用的默认版本(保持兼容)
*/
async openRepoSelect() {
return this.openRepoSelectForScope('config');
}
/**
* 弹出“上传代码”时的仓库 + 分支选择弹窗
*/
@@ -263,6 +270,9 @@ class ConfigPanel {
// Git 仓库管理
'openRepoConfig': () => this.openRepoConfig(),
'openRepoSelect': () => this.openRepoSelect(),
'openRepoSelectForProject': () => this.openRepoSelectForScope('project'),
'openRepoSelectForAircraft': () => this.openRepoSelectForScope('aircraft'),
'openRepoSelectForContainer': () => this.openRepoSelectForScope('container'),
'repoSelectedForBranches': (data) => this.handleRepoSelectedForBranches(data.repoName),
// Git 分支管理
'fetchBranches': (data) => this.fetchBranches(data.url),
@@ -587,7 +597,34 @@ class ConfigPanel {
vscode.window.showErrorMessage(`获取分支失败: ${error}`);
}
}
/**
* 根据当前作用域,把选中的分支克隆到不同层级
*/
async cloneBranches(branches) {
if (!this.currentRepoForBranches) {
vscode.window.showErrorMessage('请先通过"获取仓库"选择一个仓库');
return;
}
switch (this.currentCloneScope) {
case 'project':
await this.cloneBranchesToProjects(branches);
break;
case 'aircraft':
await this.cloneBranchesToAircrafts(branches);
break;
case 'container':
await this.cloneBranchesToContainers(branches);
break;
case 'config':
default:
await this.cloneBranchesToModuleFolders(branches);
break;
}
}
/**
* 原有逻辑:在 ConfigView 中,把分支克隆为当前容器下的 git 模块文件夹
*/
async cloneBranchesToModuleFolders(branches) {
if (!this.currentRepoForBranches) {
vscode.window.showErrorMessage('请先通过"获取仓库"选择一个仓库');
return;
@@ -635,6 +672,206 @@ class ConfigPanel {
vscode.window.showErrorMessage(`克隆分支失败: ${error}`);
}
}
/**
* ProjectView 用:把分支克隆为完整项目
* 每个分支会弹出一个路径输入框,克隆整个仓库到该路径,如果有 .dcsp-data.json 则自动加载为项目
*/
async cloneBranchesToProjects(branches) {
if (!this.currentRepoForBranches) {
vscode.window.showErrorMessage('请先通过"获取仓库"选择一个仓库');
return;
}
const url = this.currentRepoForBranches.url;
const tasks = [];
for (const branch of branches) {
const pathInput = await vscode.window.showInputBox({
prompt: `请输入分支 "${branch}" 的项目存储路径(绝对路径,系统将自动创建该文件夹)`,
placeHolder: `/path/to/your/project/${branch}`,
validateInput: (value) => {
if (!value)
return '路径不能为空';
return null;
}
});
if (!pathInput) {
vscode.window.showWarningMessage(`已取消分支 ${branch} 的克隆`);
continue;
}
tasks.push({ branch, targetPath: pathInput });
}
if (tasks.length === 0) {
return;
}
let successCount = 0;
let failCount = 0;
await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: `正在克隆 ${tasks.length} 个项目分支`,
cancellable: false
}, async (progress) => {
for (let i = 0; i < tasks.length; i++) {
const { branch, targetPath } = tasks[i];
progress.report({
message: `克隆项目分支: ${branch} (${i + 1}/${tasks.length})`
});
try {
await GitService_1.GitService.cloneRepository(url, targetPath, branch, undefined, this.currentRepoForBranches?.username, this.currentRepoForBranches?.token);
// 克隆完成后,尝试加载项目数据(如果有 .dcsp-data.json
try {
const projectId = await this.projectService.loadProjectData(targetPath);
if (projectId) {
this.currentProjectId = projectId;
successCount++;
}
else {
// 没有 dscp-data 也算成功克隆,只是不会自动出现在列表里
successCount++;
}
}
catch (err) {
console.warn('加载克隆项目数据失败:', err);
successCount++;
}
}
catch (error) {
console.error(`❌ 项目分支克隆失败: ${branch}`, error);
failCount++;
}
}
});
await vscode.commands.executeCommand('workbench.files.action.refreshFilesExplorer');
if (failCount === 0) {
vscode.window.showInformationMessage(`成功克隆 ${successCount} 个项目分支`);
}
else {
vscode.window.showWarningMessage(`项目克隆完成: ${successCount} 个成功, ${failCount} 个失败`);
}
this.currentView = 'projects';
this.updateWebview();
}
/**
* AircraftView 用:把分支克隆为当前项目下的飞行器
* 目标路径:<projectPath>/<分支名>,克隆后自动解析容器/配置/模块,并写入 dscp-data.json
*/
async cloneBranchesToAircrafts(branches) {
if (!this.currentRepoForBranches) {
vscode.window.showErrorMessage('请先通过"获取仓库"选择一个仓库');
return;
}
if (!this.currentProjectId) {
vscode.window.showErrorMessage('请先选择项目');
return;
}
const projectPath = this.projectService.getProjectPath(this.currentProjectId);
if (!projectPath) {
vscode.window.showErrorMessage('未找到项目路径');
return;
}
const url = this.currentRepoForBranches.url;
const username = this.currentRepoForBranches.username;
const token = this.currentRepoForBranches.token;
let successCount = 0;
let failCount = 0;
await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: `正在克隆飞行器分支`,
cancellable: false
}, async (progress) => {
for (let i = 0; i < branches.length; i++) {
const branch = branches[i];
const aircraftName = branch.trim();
if (!aircraftName)
continue;
const localPath = path.join(projectPath, aircraftName);
progress.report({
message: `克隆飞行器分支: ${branch} (${i + 1}/${branches.length})`
});
try {
await GitService_1.GitService.cloneRepository(url, localPath, branch, undefined, username, token);
await this.projectService.importAircraftFromExistingFolder(this.currentProjectId, aircraftName);
successCount++;
}
catch (error) {
console.error(`❌ 飞行器分支克隆失败: ${branch}`, error);
failCount++;
}
}
});
await this.saveCurrentProjectData();
await vscode.commands.executeCommand('workbench.files.action.refreshFilesExplorer');
if (failCount === 0) {
vscode.window.showInformationMessage(`成功克隆 ${successCount} 个飞行器`);
}
else {
vscode.window.showWarningMessage(`飞行器克隆完成: ${successCount} 个成功, ${failCount} 个失败`);
}
this.currentView = 'aircrafts';
this.updateWebview();
}
/**
* ContainerView 用:把分支克隆为当前飞行器下的容器
* 目标路径:<projectPath>/<当前飞行器名>/<分支名>,克隆后自动解析配置/模块,并写入 dscp-data.json
*/
async cloneBranchesToContainers(branches) {
if (!this.currentRepoForBranches) {
vscode.window.showErrorMessage('请先通过"获取仓库"选择一个仓库');
return;
}
if (!this.currentProjectId || !this.currentAircraftId) {
vscode.window.showErrorMessage('请先选择项目和飞行器');
return;
}
const projectPath = this.projectService.getProjectPath(this.currentProjectId);
if (!projectPath) {
vscode.window.showErrorMessage('未找到项目路径');
return;
}
const aircraftName = this.getAircraftName();
if (!aircraftName || aircraftName === '未知飞行器') {
vscode.window.showErrorMessage('未找到当前飞行器名称');
return;
}
const url = this.currentRepoForBranches.url;
const username = this.currentRepoForBranches.username;
const token = this.currentRepoForBranches.token;
let successCount = 0;
let failCount = 0;
await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: `正在克隆容器分支`,
cancellable: false
}, async (progress) => {
for (let i = 0; i < branches.length; i++) {
const branch = branches[i];
const containerName = branch.trim();
if (!containerName)
continue;
const localPath = path.join(projectPath, aircraftName, containerName);
progress.report({
message: `克隆容器分支: ${branch} (${i + 1}/${branches.length})`
});
try {
await GitService_1.GitService.cloneRepository(url, localPath, branch, undefined, username, token);
await this.projectService.importContainerFromExistingFolder(this.currentAircraftId, containerName);
successCount++;
}
catch (error) {
console.error(`❌ 容器分支克隆失败: ${branch}`, error);
failCount++;
}
}
});
await this.saveCurrentProjectData();
await vscode.commands.executeCommand('workbench.files.action.refreshFilesExplorer');
if (failCount === 0) {
vscode.window.showInformationMessage(`成功克隆 ${successCount} 个容器`);
}
else {
vscode.window.showWarningMessage(`容器克隆完成: ${successCount} 个成功, ${failCount} 个失败`);
}
this.currentView = 'containers';
this.updateWebview();
}
async addGitModuleFolder(url, displayName, folderName, branch, username, token) {
try {
if (!url || !url.startsWith('http')) {
@@ -1197,6 +1434,9 @@ class ConfigPanel {
// =============================================
// Webview 更新方法
// =============================================
// =============================================
// Webview 更新方法
// =============================================
updateWebview() {
if (this.isWebviewDisposed) {
console.log('⚠️ Webview 已被销毁,跳过更新');
@@ -1225,7 +1465,10 @@ class ConfigPanel {
const project = this.projectService.getProjects().find(p => p.id === this.currentProjectId);
const currentAircraft = this.projectService.getAircraftsByProject(this.currentProjectId)
.find(a => a.id === this.currentAircraftId);
const projectContainers = this.projectService.getContainersByAircraft(this.currentAircraftId);
// ✅ 只在 UI 层隐藏名为 .git 的容器
const projectContainers = this.projectService
.getContainersByAircraft(this.currentAircraftId)
.filter(c => c.name !== '.git');
return this.containerView.render({
project: project,
aircraft: currentAircraft,