bytefun
Version:
一个打通了原型设计、UI设计与代码转换、跨平台原生代码开发等的平台
1,464 lines (1,156 loc) • 191 kB
text/typescript
import * as vscode from 'vscode';
import { MESSAGE_TYPES, PageData, VSCodePluginMessage } from './messages';
import { EditorProvider } from './editorProvider';
import * as fs from 'fs';
import * as path from 'path';
import { ProjectInfo, ProjectManager, ChatSession } from './projectManager';
import { HtmlPreviewManager } from './htmlPreTab';
import { copyImagesBySimulatingUserAction } from './copyData';
import { getMultiPlatformCodeData, getNonce } from './utils';
import { handleHtmlFileChange } from './extension';
import { WorkspaceUtils } from './workspaceUtils';
// 匹配 Java 后端的数据结构
interface ConstructorParam {
name: string;
type: string;
must: boolean;
}
interface ConstructorParamDTO {
name: string;
paramList: ConstructorParam[];
}
interface CodeDTO {
name: string;
code: string;
}
export class SidebarProvider implements vscode.WebviewViewProvider {
public static readonly viewType = 'bytefunView';
private webView?: vscode.WebviewView;
private readonly projectManager: ProjectManager;
private readonly htmlPreviewManager: HtmlPreviewManager;
private isHandlingProgressRefresh: boolean = false;
private isHandlingChatClear: boolean = false;
private fileWatcher?: vscode.FileSystemWatcher; // 添加文件监听器管理
constructor(private readonly _extensionUri: vscode.Uri) {
this.projectManager = ProjectManager.getInstance();
this.htmlPreviewManager = new HtmlPreviewManager(
_extensionUri,
this.handleSaveUIConfig.bind(this),
this.handleSavePRDConfig.bind(this),
this.handleSaveBackendConfig.bind(this)
);
this.setupFileWatcher();
}
/**
* 清理资源,防止内存泄漏
*/
public dispose(): void {
try {
// 清理文件监听器
if (this.fileWatcher) {
this.fileWatcher.dispose();
this.fileWatcher = undefined;
}
// 清理webview
if (this.webView) {
// webview 会由VSCode自动清理,我们只需要清除引用
this.webView = undefined;
}
// 清理HTML预览管理器
if (this.htmlPreviewManager && typeof this.htmlPreviewManager.dispose === 'function') {
this.htmlPreviewManager.dispose();
}
} catch (error) {
console.error('❌ [ByteFun Sidebar] 清理SidebarProvider资源失败:', error);
}
}
public resolveWebviewView(
webviewView: vscode.WebviewView,
context: vscode.WebviewViewResolveContext,
_token: vscode.CancellationToken,
) {
this.webView = webviewView;
webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [this._extensionUri]
};
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
// 监听来自webview的消息
webviewView.webview.onDidReceiveMessage(
message => {
switch (message.command) {
case 'webviewReady':
this.handleWebviewReady();
break;
case 'openProjects':
// 发送消息到webviewProvider(会自动确保tab已激活)
this.sendMessageToWebviewProvider(MESSAGE_TYPES.OPEN_PROJECTS, {
source: 'sidebar',
action: 'openProjects'
}, { priority: 'high' });
break;
case 'openLibrary':
// 发送消息到webviewProvider(会自动确保tab已激活)
this.sendMessageToWebviewProvider(MESSAGE_TYPES.OPEN_LIBRARY, {
source: 'sidebar',
action: 'openLibrary'
}, { priority: 'high' });
break;
case 'refresh':
// 发送消息到webviewProvider
this.sendMessageToWebviewProvider(MESSAGE_TYPES.REFRESH, {
source: 'sidebar',
action: 'refresh'
}, { priority: 'high' });
break;
case 'openProjectPage':
// 发送消息到webviewProvider,包含完整的页面数据
this.sendMessageToWebviewProvider(MESSAGE_TYPES.OPEN_PROJECT_PAGE, {
source: 'sidebar',
action: 'openProjectPage',
pageData: message.pageData as PageData
}, { priority: 'high' });
break;
case 'tryToCreatePageTSFile':
this.projectManager.handleCreatePageTSFiles(message.projectData);
break;
case 'syncPage':
this.handleSyncPage(message.pageData);
break;
case 'syncAllPages':
this.handleSyncAllPages();
break;
case 'loadChatSessions':
this.handleLoadChatSessions(message.roleType);
break;
case 'createChatSession':
this.handleCreateChatSession(message.roleType, message.title);
break;
case 'sendMessage':
this.handleSendMessage(message.roleType, message.content);
break;
case 'deleteChatSession':
this.handleDeleteChatSession(message.sessionId);
break;
case 'clickChatLink':
this.handleClickChatLink(message.roleType, message.linkText);
break;
case 'clearChatHistory':
this.handleClearChatHistory(message.roleType);
break;
case 'loadTeamChatSessions':
this.handleLoadTeamChatSessions();
break;
case 'sendTeamMessage':
this.handleSendTeamMessage(message.content, message.senderRole);
break;
case 'clearTeamChatHistory':
this.handleClearTeamChatHistory();
break;
case 'previewDesign':
this.handlePreviewDesign();
break;
case 'openFile':
this.handleOpenFile(message.filePath);
break;
case 'openBusinessLogicModule':
this.handleOpenBusinessLogicModule(message.moduleData);
break;
case 'editProductDocument':
this.handleEditProductDocument(message.item);
break;
case 'editBusinessLogicModule':
this.handleEditBusinessLogicModule(message.moduleData);
break;
case 'openDesignPreview':
this.handleOpenDesignPreviewWithPath(message.path);
break;
case 'openPageUIDesign':
this.handleOpenPageUIDesign(message.filePath);
break;
case 'editPageUIDesign':
this.handleEditPageUIDesign(message.item);
break;
case 'openDesignSpec':
this.handleOpenDesignSpec();
break;
case 'openPrdDesignSpec':
this.handleOpenPrdDesignSpec();
break;
case 'openUIDesignThinking':
this.handleOpenUIDesignThinking(message.item);
break;
case 'syncUIDesignFile':
this.handleSyncUIDesignFile(message.item);
break;
case 'syncComplete':
this.handleSyncComplete(message.data);
break;
case 'openCodeDesign':
this.handleOpenCodeDesign(message.item);
break;
case 'openFrontendDevCode':
this.handleOpenFrontendDevCode(message.item);
break;
case 'openBackendDevCode':
this.handleOpenBackendDevCode(message.item);
break;
case 'openBackendApiDesign':
this.handleOpenBackendApiDesign();
break;
case 'showMessage':
this.handleShowMessage(message.message, message.type);
break;
case 'preViewOneAPI':
this.handlePreViewOneAPI(message.apiPath, message.apiData);
break;
default:
break;
}
},
undefined,
[]
);
// 当侧边栏视图变为可见时,打开编辑器tab
webviewView.onDidChangeVisibility(() => {
if (webviewView.visible) {
vscode.commands.executeCommand('bytefun.openEditor');
}
});
vscode.commands.executeCommand('bytefun.openEditor');
}
// 发送消息到webviewProvider1
private sendMessageToWebviewProvider<T = any>(type: string, data?: T, options?: Partial<VSCodePluginMessage<T>>) {
const message: VSCodePluginMessage<T> = {
type,
data,
id: options?.id || this.generateMessageId(),
timestamp: Date.now(),
priority: options?.priority || 'normal',
pluginId: options?.pluginId || 'bytefun-sidebar',
isResponse: options?.isResponse || false,
...options
};
// 确保ByteFun工程管理tab已打开并激活,然后发送消息
const bytefunEditorProvider = (global as any).bytefunEditorProvider;
if (bytefunEditorProvider) {
bytefunEditorProvider.ensureWebviewActiveAndExecute(() => {
const success = bytefunEditorProvider.sendMessageToWebview(message);
if (success) {
} else {
}
});
}
return true; // 返回true表示已启动发送流程
}
// 接收来自webviewProvider的转发消息
public handleForwardedMessage(type: string, data: any) {
// 处理同步逻辑完成消息
if (type === 'synCodeToEditorDone' && data) {
// 发送消息给webviewProvider
// this.sendMessageToWebviewProvider('syncLogicComplete', data);
// 同时发送消息给sidebar的webview,让前端能够收到并更新UI
this.sendMessageToWebview('syncLogicComplete', data);
return;
}
// 特殊处理 mcpMessage 类型的消息
if (type === 'mcpMessage' && data) {
// 对于 mcpMessage,只通过 handleMcpMessage 处理
// 不直接转发给 webview,避免重复
this.handleMcpMessage(data);
return;
}
// 特殊处理 syncComplete 类型的消息
if (type === 'syncComplete' && data) {
// 对于 syncComplete,直接调用处理方法
this.handleSyncComplete(data);
return;
}
// 其他类型的消息正常转发给 webview
if (this.webView) {
const message = {
type: type,
data: data
};
this.webView.webview.postMessage(message);
} else {
}
}
// 生成消息ID
private generateMessageId(): string {
return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
// 处理webview准备就绪事件
private handleWebviewReady(): void {
// 检查当前工作区是否存在项目文件
const isProjectExists = this.checkProjectFileExists();
// 发送项目状态到webview
this.sendProjectStatusToWebview(isProjectExists);
// 初始化时检查设计预览链接的显示状态
this.updateDesignPreviewVisibility();
}
// 检查项目文件是否存在
private checkProjectFileExists(): boolean {
try {
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (!workspaceRoot) {
return false;
}
const projectJsonPath = path.join(workspaceRoot, '.bytefun', 'project.json');
const exists = fs.existsSync(projectJsonPath);
return exists;
} catch (error) {
console.error('📱 [ByteFun Sidebar] 检查项目文件失败:', error);
return false;
}
}
// 发送项目状态到webview
private sendProjectStatusToWebview(isProjectExists: boolean): void {
if (this.webView) {
try {
this.webView.webview.postMessage({
type: 'projectStatusUpdate',
data: {
isProjectExists: isProjectExists
}
});
} catch (error) {
console.error('📱 [ByteFun Sidebar] 发送项目状态消息失败:', error);
}
} else {
}
}
// 公共方法:处理工作区变化时的UI更新
public handleWorkspaceChange(): void {
// 检查新工作区的项目文件状态
const isProjectExists = this.checkProjectFileExists();
// 发送更新的项目状态到webview
this.sendProjectStatusToWebview(isProjectExists);
}
// 处理单个页面同步
public async handleSyncPage(pageData: any): Promise<void> {
try {
// 获取当前工作区
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (!workspaceRoot) {
console.error('❌ [ByteFun Sidebar] 未找到工作区');
vscode.window.showErrorMessage('未找到工作区');
return;
}
// 读取项目配置获取treeProjectId
const projectJsonPath = path.join(workspaceRoot, '.bytefun', 'project.json');
let treeProjectId: number | undefined;
let projectID = '';
try {
if (fs.existsSync(projectJsonPath)) {
const projectJsonContent = fs.readFileSync(projectJsonPath, 'utf8');
const projectConfig = JSON.parse(projectJsonContent);
treeProjectId = projectConfig.treeProjectId;
projectID = projectConfig.projectID || '';
} else {
console.error('❌ [ByteFun Sidebar] project.json文件不存在');
vscode.window.showErrorMessage('项目配置文件不存在');
return;
}
} catch (error) {
console.error('❌ [ByteFun Sidebar] 读取项目配置失败:', error);
vscode.window.showErrorMessage('读取项目配置失败');
return;
}
// 构建页面文件夹名称
const pageNameEn = pageData.enName || pageData.name;
let lowerPageFileName = pageNameEn.charAt(0).toLowerCase() + pageNameEn.slice(1);
// 查找页面文件夹
const srcPath = path.join(workspaceRoot, 'src');
const pageFolderPath = this.findPageDirectory(srcPath, lowerPageFileName);
if (!pageFolderPath) {
console.error(`❌ [ByteFun Sidebar] 未找到页面文件夹: ${lowerPageFileName}`);
vscode.window.showErrorMessage(`未找到页面文件夹: ${lowerPageFileName}`);
return;
}
// 读取文件夹下所有.ts文件
const tsFiles = this.readTsFilesInDirectory(pageFolderPath, srcPath);
if (tsFiles.length === 0) {
console.warn('⚠️ [ByteFun Sidebar] 页面文件夹中没有找到.ts文件');
vscode.window.showWarningMessage('页面文件夹中没有找到.ts文件');
return;
}
// 构建FinalAstCreateCriteria对象
const classMap = this.extractConstructorParams(tsFiles);
// 查找并提取 navigateTo 调用的页面类,合并到 classMap 中
await this.extractAndMergeNavigateToPages(tsFiles, classMap, srcPath);
const finalAstCreateCriteria = {
projectID: projectID,
treeProjectId: treeProjectId,
classMap: classMap,
codeList: tsFiles,
pageNameEn: pageNameEn,
projectNodeId: treeProjectId!,
generateCodeVO: getMultiPlatformCodeData()
};
// 发送消息到editorProvider
const bytefunEditorProvider = (global as any).bytefunEditorProvider;
if (bytefunEditorProvider) {
const success = bytefunEditorProvider.sendMessageToWebview({
type: 'synCodeToEditor',
data: finalAstCreateCriteria
});
if (success) {
} else {
console.error('❌ [ByteFun Sidebar] 发送同步消息失败');
vscode.window.showErrorMessage('发送同步消息失败');
}
} else {
console.error('❌ [ByteFun Sidebar] editorProvider不可用');
vscode.window.showErrorMessage('编辑器提供者不可用');
}
} catch (error) {
console.error('❌ [ByteFun Sidebar] 同步页面失败:', error);
vscode.window.showErrorMessage(`同步页面失败: ${error}`);
}
}
// 处理全部页面同步
private async handleSyncAllPages(): Promise<void> {
try {
// 获取当前工作区
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (!workspaceRoot) {
console.error('❌ [ByteFun Sidebar] 未找到工作区');
vscode.window.showErrorMessage('未找到工作区');
return;
}
const srcPath = path.join(workspaceRoot, 'src');
// 读取项目配置获取treeProjectId
const projectJsonPath = path.join(workspaceRoot, '.bytefun', 'project.json');
let treeProjectId: number | undefined;
try {
if (fs.existsSync(projectJsonPath)) {
const projectJsonContent = fs.readFileSync(projectJsonPath, 'utf8');
const projectConfig = JSON.parse(projectJsonContent);
treeProjectId = projectConfig.treeProjectId;
} else {
console.error('❌ [ByteFun Sidebar] project.json文件不存在');
vscode.window.showErrorMessage('项目配置文件不存在');
return;
}
} catch (error) {
console.error('❌ [ByteFun Sidebar] 读取项目配置失败:', error);
vscode.window.showErrorMessage('读取项目配置失败');
return;
}
// 扫描src目录下所有的.ts文件
const allTsFiles = this.readAllTsFilesInSrc(srcPath);
if (allTsFiles.length === 0) {
console.warn('⚠️ [ByteFun Sidebar] src目录中没有找到.ts文件');
vscode.window.showWarningMessage('src目录中没有找到.ts文件');
return;
}
// 构建FinalAstCreateCriteria对象
const classMap = this.extractConstructorParams(allTsFiles);
const finalAstCreateCriteria = {
classMap: classMap,
codeList: allTsFiles,
pageNameEn: '',
projectNodeId: treeProjectId!
};
// 发送消息到editorProvider
const bytefunEditorProvider = (global as any).bytefunEditorProvider;
if (bytefunEditorProvider) {
const success = bytefunEditorProvider.sendMessageToWebview({
type: 'synCodeToEditor',
data: finalAstCreateCriteria
});
if (success) {
} else {
console.error('❌ [ByteFun Sidebar] 发送全部同步消息失败');
vscode.window.showErrorMessage('发送全部同步消息失败');
}
} else {
console.error('❌ [ByteFun Sidebar] editorProvider不可用');
vscode.window.showErrorMessage('编辑器提供者不可用');
}
} catch (error) {
console.error('❌ [ByteFun Sidebar] 全部同步失败:', error);
vscode.window.showErrorMessage(`全部同步失败: ${error}`);
}
}
// 在指定目录下查找页面文件夹
private findPageDirectory(searchDir: string, targetFolderName: string): string | null {
try {
if (!fs.existsSync(searchDir)) {
return null;
}
const items = fs.readdirSync(searchDir, { withFileTypes: true });
for (const item of items) {
if (item.isDirectory()) {
if (item.name === targetFolderName) {
return path.join(searchDir, item.name);
}
// 递归搜索子文件夹
const subDirPath = path.join(searchDir, item.name);
const result = this.findPageDirectory(subDirPath, targetFolderName);
if (result) {
return result;
}
}
}
return null;
} catch (error) {
console.error('❌ [ByteFun Sidebar] 搜索页面文件夹时出错:', error);
return null;
}
}
// 读取指定目录下的所有.ts文件
private readTsFilesInDirectory(dirPath: string, srcPath: string): Array<{ name: string, code: string }> {
const tsFiles: Array<{ name: string, code: string }> = [];
try {
if (!fs.existsSync(dirPath)) {
return tsFiles;
}
const items = fs.readdirSync(dirPath);
for (const item of items) {
const itemPath = path.join(dirPath, item);
const stat = fs.statSync(itemPath);
if (stat.isFile() && item.endsWith('.ts')) {
try {
const code = fs.readFileSync(itemPath, 'utf8');
const relativePath = path.relative(srcPath, itemPath);
const name = relativePath.replace(/\.ts$/, '').replace(/\\/g, '/'); // 移除.ts后缀并统一路径分隔符
tsFiles.push({ name, code });
} catch (error) {
console.error(`❌ [ByteFun Sidebar] 读取文件失败: ${item}`, error);
}
}
}
} catch (error) {
console.error('❌ [ByteFun Sidebar] 读取目录失败:', error);
}
return tsFiles;
}
// 读取src目录下所有的.ts文件(递归)
private readAllTsFilesInSrc(srcPath: string): Array<{ name: string, code: string }> {
const tsFiles: Array<{ name: string, code: string }> = [];
const readTsFilesRecursively = (currentPath: string) => {
try {
if (!fs.existsSync(currentPath)) {
return;
}
const items = fs.readdirSync(currentPath);
for (const item of items) {
const itemPath = path.join(currentPath, item);
const stat = fs.statSync(itemPath);
if (stat.isDirectory()) {
// 递归读取子目录
readTsFilesRecursively(itemPath);
} else if (stat.isFile() && item.endsWith('.ts')) {
try {
const code = fs.readFileSync(itemPath, 'utf8');
const relativePath = path.relative(srcPath, itemPath);
const name = relativePath.replace(/\.ts$/, '').replace(/\\/g, '/'); // 移除.ts后缀并统一路径分隔符
tsFiles.push({ name, code });
} catch (error) {
console.error(`❌ [ByteFun Sidebar] 读取文件失败: ${item}`, error);
}
}
}
} catch (error) {
console.error('❌ [ByteFun Sidebar] 递归读取目录失败:', error);
}
};
readTsFilesRecursively(srcPath);
return tsFiles;
}
// 处理加载聊天会话
private async handleLoadChatSessions(roleType: 'product' | 'design' | 'frontEndDev' | 'backEndDev' | 'operations'): Promise<void> {
try {
// 特殊处理产品角色和设计角色 - 使用新的文档读取方式
if (roleType === 'product') {
await this.handleLoadProductItems();
return;
}
if (roleType === 'design') {
await this.handleLoadDesignItems();
return;
}
// 特殊处理前端开发和后端开发角色 - 使用新的代码进度读取方式
if (roleType === 'frontEndDev') {
await this.handleLoadFrontendDevItems();
return;
}
if (roleType === 'backEndDev') {
await this.handleLoadBackendDevItems();
return;
}
const uid = this.projectManager.getUID();
const projectID = this.projectManager.getCurrentProjectID();
if (!uid || !projectID) {
this.sendChatDataToWebview(roleType, []);
return;
}
// 其他角色的进度刷新逻辑已移至item方式
if (this.isHandlingChatClear) {
}
const sessions = this.projectManager.getUserProjectChatSessions(uid, projectID);
const filteredSessions = sessions.filter(session => session.roleType === roleType);
this.sendChatDataToWebview(roleType, filteredSessions);
} catch (error) {
console.error('🔄 [流程-1] [后端] ❌ 加载聊天会话失败:', error);
this.sendChatDataToWebview(roleType, []);
}
}
// 处理产品角色内容加载
private async handleLoadProductItems(): Promise<void> {
try {
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (!workspaceRoot) {
this.sendProductItemsToWebview([]);
return;
}
const items: any[] = [];
// 检查是否存在产品需求文档
const productDocPath = path.join(workspaceRoot, '.bytefun', '产品需求文档.md');
if (fs.existsSync(productDocPath)) {
items.push({
type: 'document',
title: '产品需求文档',
path: productDocPath,
icon: '📋'
});
}
// 检查是否存在整体模块分析进度.json
const moduleProgressPath = path.join(workspaceRoot, 'doc', '整体模块分析进度.json');
if (fs.existsSync(moduleProgressPath)) {
try {
const progressData = JSON.parse(fs.readFileSync(moduleProgressPath, 'utf8'));
if (progressData && Array.isArray(progressData.modules)) {
// 遍历modules数组,为每个模块创建一个item
let index = 1; // 序号计数器
progressData.modules.forEach((module: any) => {
if (module.moduleNameCN) {
const statusIcon = module.status === 'completed' ? '✅' : '🔄';
items.push({
type: 'business_logic',
title: `${index}.${module.moduleNameCN}业务设计 ${statusIcon}`,
moduleData: module,
icon: '' // 不显示左侧图标
});
index++; // 递增序号
}
});
}
} catch (error) {
console.error('解析模块分析进度文件失败:', error);
}
}
this.sendProductItemsToWebview(items);
} catch (error) {
console.error('❌ [产品角色] 加载文档项目失败:', error);
this.sendProductItemsToWebview([]);
}
}
// 处理设计角色内容加载
private async handleLoadDesignItems(): Promise<void> {
try {
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (!workspaceRoot) {
this.sendDesignItemsToWebview([]);
return;
}
const items: any[] = [];
// 检查src/index.html是否存在,存在才添加预览设计稿项目
const indexHtmlPath = path.join(workspaceRoot, 'src', 'index.html');
if (fs.existsSync(indexHtmlPath)) {
items.push({
type: 'design_preview',
title: '预览设计稿',
subtitle: '查看设计规范',
icon: '🎨',
path: 'src/index.html'
});
}
// 检查是否存在UI设计进度.json
const uiProgressPath = path.join(workspaceRoot, 'doc', 'UI设计进度.json');
if (fs.existsSync(uiProgressPath)) {
try {
const progressData = JSON.parse(fs.readFileSync(uiProgressPath, 'utf8'));
if (progressData && Array.isArray(progressData.modules)) {
// 遍历modules数组,为每个页面创建一个item
progressData.modules.forEach((module: any) => {
if (module.pages && Array.isArray(module.pages)) {
module.pages.forEach((page: any) => {
const statusIcon = page.status === 'completed' ? '✅' : '🔄';
items.push({
type: 'page_ui_design',
title: `${page.id}.${module.moduleNameCN}-${page.pageNameCN} ${statusIcon}`,
moduleData: module,
pageData: page,
filePath: `src/${module.moduleNameEN}/${page.pageNameEN}/${page.pageNameEN}.html`,
icon: '' // 不显示左侧图标
});
});
}
});
}
} catch (error) {
console.error('解析UI设计进度文件失败:', error);
}
}
this.sendDesignItemsToWebview(items);
} catch (error) {
console.error('❌ [设计角色] 加载设计项目失败:', error);
this.sendDesignItemsToWebview([]);
}
}
// 发送产品项目数据到前端
private sendProductItemsToWebview(items: any[]): void {
this.sendMessageToWebview('productItems', items);
}
// 发送设计项目数据到前端
private sendDesignItemsToWebview(items: any[]): void {
this.sendMessageToWebview('designItems', items);
}
// 处理加载前端开发项目
private async handleLoadFrontendDevItems(): Promise<void> {
try {
const items: any[] = [];
const workspaceRoot = WorkspaceUtils.getProjectRootPath()!;
if (!workspaceRoot) {
this.sendFrontendDevItemsToWebview([]);
return;
}
const frontendProgressPath = path.join(workspaceRoot, 'doc/前端代码开发进度.json');
if (fs.existsSync(frontendProgressPath)) {
try {
const progressContent = fs.readFileSync(frontendProgressPath, 'utf-8');
const progressData = JSON.parse(progressContent);
if (progressData.modules && Array.isArray(progressData.modules)) {
for (const module of progressData.modules) {
if (module.pages && Array.isArray(module.pages)) {
for (const page of module.pages) {
const codeStatusIcon = page.codeStatus === 'completed' ? '✅' : '🔄';
items.push({
type: 'frontend_dev',
title: `${page.id}.${module.moduleNameCN}-${page.pageNameCN} ${codeStatusIcon}`,
moduleData: module,
pageData: page,
icon: ''
});
}
}
}
}
} catch (error) {
console.error('❌ [前端开发角色] 解析前端代码开发进度.json失败:', error);
}
} else {
}
this.sendFrontendDevItemsToWebview(items);
} catch (error) {
console.error('❌ [前端开发角色] 加载前端开发项目失败:', error);
this.sendFrontendDevItemsToWebview([]);
}
}
// 处理加载后端开发项目
private async handleLoadBackendDevItems(): Promise<void> {
try {
const items: any[] = [];
const workspaceRoot = WorkspaceUtils.getProjectRootPath()!;
if (!workspaceRoot) {
this.sendBackendDevItemsToWebview([]);
return;
}
// 检查.bytefun/backendDesign.html是否存在,存在才添加后端接口设计项目
const backendDesignHtmlPath = path.join(workspaceRoot, '.bytefun', 'backendDesign.html');
if (fs.existsSync(backendDesignHtmlPath)) {
items.push({
type: 'backend_api_design',
title: '后端接口设计',
subtitle: '管理后端API接口',
icon: '🔧',
path: '.bytefun/backendDesign.html'
});
}
const backendProgressPath = path.join(workspaceRoot, 'doc/后端代码开发进度.json');
if (fs.existsSync(backendProgressPath)) {
try {
const progressContent = fs.readFileSync(backendProgressPath, 'utf-8');
const progressData = JSON.parse(progressContent);
if (progressData.modules && Array.isArray(progressData.modules)) {
for (const module of progressData.modules) {
if (module.apis && Array.isArray(module.apis)) {
for (const api of module.apis) {
const codeStatusIcon = api.codeStatus === 'completed' ? '✅' : '🔄';
items.push({
type: 'backend_dev',
title: `${api.id}.${module.moduleNameCN}-${api.name} ${codeStatusIcon}`,
moduleData: module,
apiData: api,
icon: ''
});
}
}
}
}
} catch (error) {
console.error('❌ [后端开发角色] 解析后端代码开发进度.json失败:', error);
}
} else {
}
this.sendBackendDevItemsToWebview(items);
} catch (error) {
console.error('❌ [后端开发角色] 加载后端开发项目失败:', error);
this.sendBackendDevItemsToWebview([]);
}
}
// 发送前端开发项目数据到前端
private sendFrontendDevItemsToWebview(items: any[]): void {
this.sendMessageToWebview('frontendDevItems', { items });
}
// 发送后端开发项目数据到前端
private sendBackendDevItemsToWebview(items: any[]): void {
this.sendMessageToWebview('backendDevItems', { items });
}
// 处理打开产品需求文档请求
private async handleOpenFile(filePath: string): Promise<void> {
try {
if (!filePath) {
console.error('❌ [产品角色] 文件路径为空');
return;
}
// 对于产品需求文档,使用文件搜索功能
const matchingFile = this.findMatchingDocFile('产品需求文档');
if (matchingFile) {
// 使用相对路径调用openFileInPreview
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (workspaceRoot) {
const relativePath = path.relative(workspaceRoot, matchingFile);
await this.openFileInPreview(relativePath);
} else {
await this.openFileInPreview(matchingFile);
}
} else {
vscode.window.showWarningMessage('未找到产品需求文档相关的文件');
}
} catch (error) {
console.error('❌ [产品角色] 打开文件失败:', error);
vscode.window.showErrorMessage(`打开文件失败: ${error}`);
}
}
// 在doc文件夹中搜索匹配的文件
private findMatchingDocFile(searchTerm: string): string | null {
try {
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (!workspaceRoot) {
return null;
}
const docDir = path.join(workspaceRoot, 'doc');
if (!fs.existsSync(docDir)) {
return null;
}
// 读取doc文件夹中的所有文件
const files = fs.readdirSync(docDir);
// 查找包含搜索词的文件
for (const file of files) {
if (file.toLowerCase().includes(searchTerm.toLowerCase()) && file.endsWith('.md')) {
const filePath = path.join(docDir, file);
return filePath;
}
}
return null;
} catch (error) {
console.error('❌ [产品角色] 搜索文件失败:', error);
return null;
}
}
// 处理打开业务逻辑模块请求(预览)
private async handleOpenBusinessLogicModule(moduleData: any): Promise<void> {
try {
if (!moduleData) {
console.error('❌ [产品角色] 模块数据为空');
return;
}
// 使用moduleNameEN搜索匹配的业务逻辑JSON文件
const moduleNameEN = moduleData.moduleNameEN;
if (moduleNameEN) {
const matchingJsonFile = this.findModuleBusinessLogicFile(moduleNameEN);
if (matchingJsonFile) {
// 读取JSON文件内容
const jsonContent = fs.readFileSync(matchingJsonFile, 'utf8');
// 生成唯一标识和标题用于预览
const uniqueKey = `module-${moduleNameEN}-business-logic`;
const displayTitle = `${moduleData.moduleNameCN || moduleNameEN}模块业务逻辑设计`;
// 预览prdDesign.html,传入JSON内容、fromWhere参数、唯一标识和标题
const specPath = '.bytefun/prdDesign.html';
await this.htmlPreviewManager.openHtmlFileInBrowser(specPath, jsonContent, 'module', uniqueKey, displayTitle);
} else {
vscode.window.showWarningMessage(`未找到模块 "${moduleNameEN}" 对应的业务逻辑JSON文件`);
}
} else {
vscode.window.showWarningMessage('模块英文名为空,无法搜索相关业务逻辑文件');
}
} catch (error) {
console.error('❌ [产品角色] 处理业务逻辑模块失败:', error);
vscode.window.showErrorMessage(`处理业务逻辑模块失败: ${error}`);
}
}
// 查找模块业务逻辑JSON文件
private findModuleBusinessLogicFile(moduleNameEN: string): string | null {
try {
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (!workspaceRoot) {
return null;
}
const bytefunDir = path.join(workspaceRoot, '.bytefun');
if (!fs.existsSync(bytefunDir)) {
return null;
}
// 读取.bytefun文件夹中的所有文件
const files = fs.readdirSync(bytefunDir);
// 查找包含'-{moduleNameEN}-module-business-logic'的JSON文件
const searchPattern = `-${moduleNameEN}-module-business-logic`;
for (const file of files) {
if (file.includes(searchPattern) && file.endsWith('.json')) {
const filePath = path.join(bytefunDir, file);
return filePath;
}
}
return null;
} catch (error) {
console.error('❌ [产品角色] 搜索业务逻辑文件失败:', error);
return null;
}
}
// 处理编辑产品需求文档请求
private async handleEditProductDocument(item: any): Promise<void> {
try {
// 搜索产品需求文档文件
const matchingFile = this.findMatchingDocFile('产品需求文档');
if (matchingFile) {
// 使用相对路径调用openFileInEditor
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (workspaceRoot) {
const relativePath = path.relative(workspaceRoot, matchingFile);
await this.openFileInEditor(relativePath);
} else {
await this.openFileInEditor(matchingFile);
}
} else {
vscode.window.showWarningMessage('未找到产品需求文档相关的文件');
}
} catch (error) {
console.error('❌ [产品角色] 编辑产品需求文档失败:', error);
vscode.window.showErrorMessage(`编辑产品需求文档失败: ${error}`);
}
}
// 处理编辑业务逻辑模块请求
private async handleEditBusinessLogicModule(moduleData: any): Promise<void> {
try {
if (!moduleData) {
console.error('❌ [产品角色] 模块数据为空');
return;
}
// 使用moduleNameCN搜索匹配的文档文件
const moduleNameCN = moduleData.moduleNameCN;
if (moduleNameCN) {
const matchingFile = this.findMatchingDocFile(moduleNameCN);
if (matchingFile) {
// 使用相对路径调用openFileInEditor
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (workspaceRoot) {
const relativePath = path.relative(workspaceRoot, matchingFile);
await this.openFileInEditor(relativePath);
} else {
await this.openFileInEditor(matchingFile);
}
} else {
vscode.window.showWarningMessage(`未找到 "${moduleNameCN}" 相关的文档文件`);
}
} else {
vscode.window.showWarningMessage('模块名称为空,无法搜索相关文档');
}
} catch (error) {
console.error('❌ [产品角色] 编辑业务逻辑模块失败:', error);
vscode.window.showErrorMessage(`编辑业务逻辑模块失败: ${error}`);
}
}
// 处理打开指定路径的设计稿预览请求
private async handleOpenDesignPreviewWithPath(path: string): Promise<void> {
try {
await this.htmlPreviewManager.openHtmlFileInBrowser(path);
} catch (error) {
console.error('❌ [设计角色] 预览设计稿失败:', error);
vscode.window.showErrorMessage(`预览设计稿失败: ${error}`);
}
}
// 处理打开页面UI设计请求
private async handleOpenPageUIDesign(filePath: string): Promise<void> {
try {
if (!filePath) {
console.error('❌ [设计角色] 文件路径为空');
return;
}
await this.openFileInEditor(filePath);
} catch (error) {
console.error('❌ [设计角色] 打开页面UI设计失败:', error);
vscode.window.showErrorMessage(`打开页面UI设计失败: ${error}`);
}
}
// 处理编辑页面UI设计请求
private async handleEditPageUIDesign(item: any): Promise<void> {
try {
// 从item中提取pageEnName
const pageEnName = item.pageData?.pageNameEN || item.pageData?.pageEnName;
if (!pageEnName) {
console.error('❌ [设计角色] 无法从item中获取页面英文名:', item);
vscode.window.showErrorMessage('无法获取页面英文名,请检查数据结构');
return;
}
// 复用HTML预览中的页面查找和打开逻辑
await this.findPageDataAndOpenProjectPage(pageEnName);
} catch (error) {
console.error('❌ [设计角色] 编辑页面UI设计失败:', error);
vscode.window.showErrorMessage(`编辑页面UI设计失败: ${error}`);
}
}
// 处理打开设计规范请求
private async handleOpenDesignSpec(): Promise<void> {
try {
const specPath = '.bytefun/uiDesign.html';
const configPath = '.bytefun/ui-design-config.json';
// 读取配置文件
let configData = '';
try {
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (workspaceRoot) {
const fs = await import('fs');
const path = await import('path');
const fullConfigPath = path.join(workspaceRoot, configPath);
if (fs.existsSync(fullConfigPath)) {
configData = fs.readFileSync(fullConfigPath, 'utf8');
} else {
}
}
} catch (configError) {
console.error('❌ [设计角色] 读取配置文件失败:', configError);
}
// 生成唯一标识和标题用于预览
const uniqueKey = 'ui-design-spec';
const displayTitle = '设计规范';
await this.htmlPreviewManager.openHtmlFileInBrowser(specPath, configData, undefined, uniqueKey, displayTitle);
} catch (error) {
console.error('❌ [设计角色] 打开设计规范失败:', error);
vscode.window.showErrorMessage(`打开设计规范失败: ${error}`);
}
}
// 处理打开产品需求设计规范请求
public async handleOpenPrdDesignSpec(): Promise<void> {
try {
const specPath = '.bytefun/prdDesign.html';
const configPath = '.bytefun/prd-design-config.json';
// 读取配置文件
let configData = '';
try {
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (workspaceRoot) {
const fs = await import('fs');
const path = await import('path');
const fullConfigPath = path.join(workspaceRoot, configPath);
if (fs.existsSync(fullConfigPath)) {
configData = fs.readFileSync(fullConfigPath, 'utf8');
} else {
}
}
} catch (configError) {
console.error('❌ [产品角色] 读取配置文件失败:', configError);
}
// 生成唯一标识和标题用于预览
const uniqueKey = 'prd-design-main';
const displayTitle = '产品需求文档';
await this.htmlPreviewManager.openHtmlFileInBrowser(specPath, configData, 'prd', uniqueKey, displayTitle);
} catch (error) {
console.error('❌ [产品角色] 打开产品需求设计规范失败:', error);
vscode.window.showErrorMessage(`打开产品需求设计规范失败: ${error}`);
}
}
// 处理打开预览设计稿请求(公开方法)
public async handleOpenDesignPreview(): Promise<void> {
try {
const workspaceRoot = WorkspaceUtils.getProjectRootPath();
if (!workspaceRoot) {
vscode.window.showWarningMessage('未找到工作区');
return;
}
const indexHtmlPath = path.join(workspaceRoot, 'src', 'index.html');
if (fs.existsSync(indexHtmlPath)) {
// 生成唯一标识和标题用于预览
const uniqueKey = 'design-preview-main';
const displayTitle = 'UI设计稿';
await this.htmlPreviewManager.openHtmlFileInBrowser('src/index.html', undefined, undefined, uniqueKey, displayTitle);
} else {
vscode.window.showWarningMessage('src/index.html文件不存在');
}
} catch (error) {
console.error('❌ [ByteFun Sidebar] 预览设计稿失败:', error);
vscode.window.showErrorMessage('预览设计稿失败');
}
}
// 处理打开UI设计思路请求
private async handleOpenUIDesignThinking(item: any): Promise<void> {
try {
// 从item的moduleData和pageData中提取moduleNameEN和pageNameEN
const moduleNameEN = item.moduleData?.moduleNameEN;
const pageNameEN = item.pageData?.pageNameEN;
if (!moduleNameEN || !pageNameEN) {
throw new Error('无法获取模块或页面的英文名称');
}
const thinkingPath = `src/${moduleNameEN}/${pageNameEN}/uiDesignThinking.md`;
await this.openFileInEditor(thinkingPath);
} catch (error) {
console.error('❌ [设计角色] 打开UI设计思路失败:', error);
vscode.window.showErrorMessage(`打开UI设计思路失败: ${error}`);
}
}
// 处理同步UI设计文件请求
private async handleSyncUIDesignFile(item: any): Promise<void> {
try {
// 从item的moduleData和pageData中提取moduleNameEN和pageNameEN
const moduleNameEN = item.moduleData?.moduleNameEN;
const pageNameEN = item.pageData?.pageNameEN;
if (!moduleNameEN || !pageNameEN) {
throw new Error('无法获取模块或页面的英文名称');
}
// 构建设计文件路径
const designFilePath = `src/${moduleNameEN}/${pageNameEN}/${pageNameEN}.html`;
// 调用EditorProvider的handleUIDesignFileCreated方法
const bytefunEditorProvider = (global as any).bytefunEditorProvider;
if (bytefunEditorProvider && bytefunEditorProvider instanceof EditorProvider) {
bytefunEditorProvider.handleUIDesignFileCreated(designFilePath, item.pageData, item.pageData.pageNameEN);
} else {
throw new Error('EditorProvider实例不可用,无法同步UI设计文件');
}
} catch (error) {
console.error('❌ [设计角色] 同步UI设计文件失败:', error);
vscode.window.showErrorMessage(`同步UI设计文件失败: ${error}`);
}
}
// 处理同步完成消息
private async handleSyncComplete(data: any): Promise<void> {
try {
// 发送消息给webview,让其切换同步按钮图标回原状态
this.sendMessageToWebview('syncButtonComplete', data);
// 显示成功提示
vscode.window.showInformationMessage('同步完成!');
} catch (error) {
console.error('❌ [设计角色] 处理同步完成失败:', error);
}
}
// 处理打开代码设计请求
private async handleOpenCodeDesign(item: any): Promise<void> {
try {
// 从item的moduleData和pageData中提取moduleNameEN和pageNameEN
const moduleNameEN = item.moduleData?.moduleNameEN;
const pageNameEN = item.pageData?.pageNameEN;
if (!moduleNameEN || !pageNameEN) {
throw new Error('无法获取模块或页面的英文名称');
}
const codeDe