UNPKG

bytefun

Version:

一个打通了原型设计、UI设计与代码转换、跨平台原生代码开发等的平台

1,464 lines (1,156 loc) 191 kB
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