UNPKG

bytefun

Version:

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

1,627 lines (1,356 loc) 76.5 kB
const vscode = acquireVsCodeApi(); // 事件监听器管理 const eventListeners = []; /** * 添加事件监听器并记录,以便后续清理 */ function addManagedEventListener(element, event, handler, options) { if (element) { element.addEventListener(event, handler, options); eventListeners.push({ element, event, handler, options }); } } /** * 清理所有注册的事件监听器 */ function cleanupEventListeners() { eventListeners.forEach(({ element, event, handler, options }) => { try { if (element && element.removeEventListener) { element.removeEventListener(event, handler, options); } } catch (error) { console.error('❌ [ByteFun Sidebar] 清理事件监听器失败:', error); } }); eventListeners.length = 0; // 清空数组 } /** * 页面卸载时清理资源 */ function handlePageUnload() { cleanupEventListeners(); } // 监听页面卸载事件 addManagedEventListener(window, 'beforeunload', handlePageUnload); addManagedEventListener(window, 'unload', handlePageUnload); // 获取之前保存的状态 let state = vscode.getState() || { selectedPageId: null, activeMenuId: null, activeTab: 'workspace' }; // 页面加载时恢复选中状态 addManagedEventListener(document, 'DOMContentLoaded', function () { if (state.selectedPageId) { selectPage(state.selectedPageId); } if (state.activeMenuId) { setActiveMenu(state.activeMenuId); } if (state.activeTab) { switchTab(state.activeTab); } // 添加事件监听器 const projectsBtn = document.getElementById('projects-btn'); const libraryBtn = document.getElementById('library-btn'); const refreshBtn = document.getElementById('refresh-btn'); const syncAllBtn = document.getElementById('sync-all-btn'); if (projectsBtn) { addManagedEventListener(projectsBtn, 'click', handleProjectClick); } if (libraryBtn) { addManagedEventListener(libraryBtn, 'click', handleLibraryClick); } if (refreshBtn) { addManagedEventListener(refreshBtn, 'click', handleRefreshClick); } if (syncAllBtn) { addManagedEventListener(syncAllBtn, 'click', handleSyncAllClick); } // 添加 tab 切换事件监听器 const tabItems = document.querySelectorAll('.tab-item'); tabItems.forEach(tab => { addManagedEventListener(tab, 'click', function () { const tabId = this.getAttribute('data-tab'); switchTab(tabId); }); }); // 添加发送按钮和输入框事件监听器(排除产品角色,产品角色现在使用item界面) const chatRoles = ['design', 'frontEndDev', 'backEndDev', 'operations']; chatRoles.forEach(role => { const sendButton = document.getElementById(`${role}-send`); const input = document.getElementById(`${role}-input`); if (sendButton && input) { // 发送按钮点击事件 addManagedEventListener(sendButton, 'click', function () { sendChatMessage(role); }); // 输入框回车事件 addManagedEventListener(input, 'keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendChatMessage(role); } }); // 输入框自动调整高度 addManagedEventListener(input, 'input', function () { this.style.height = 'auto'; this.style.height = Math.min(this.scrollHeight, 120) + 'px'; }); } }); // 替换刷新按钮的emoji图标为PNG图标 const refreshProductButtons = document.querySelectorAll('.refresh-product-button'); refreshProductButtons.forEach((button) => { // 清空原有内容 button.innerHTML = ''; // 创建图标img元素 const refreshIcon = document.createElement('img'); refreshIcon.src = window.ICON_URIS.refreshIcon; refreshIcon.className = 'refresh-icon'; refreshIcon.alt = '刷新'; refreshIcon.style.width = '17px'; refreshIcon.style.height = '14px'; refreshIcon.style.opacity = '0.8'; refreshIcon.style.transition = 'opacity 0.2s ease, transform 0.2s ease'; button.appendChild(refreshIcon); // 添加hover效果 button.addEventListener('mouseenter', function () { refreshIcon.style.opacity = '1'; refreshIcon.style.transform = 'rotate(180deg)'; }); button.addEventListener('mouseleave', function () { refreshIcon.style.opacity = '0.8'; refreshIcon.style.transform = 'rotate(0deg)'; }); }); // 为产品角色和设计角色添加刷新按钮事件监听器 refreshProductButtons.forEach((button, index) => { addManagedEventListener(button, 'click', function () { const panel = button.closest('.tab-panel'); if (panel) { const role = panel.getAttribute('data-role'); if (role === 'product') { vscode.postMessage({ command: 'loadChatSessions', roleType: 'product' }); } else if (role === 'design') { vscode.postMessage({ command: 'loadChatSessions', roleType: 'design' }); } else if (role === 'frontEndDev') { vscode.postMessage({ command: 'loadChatSessions', roleType: 'frontEndDev' }); } else if (role === 'backEndDev') { vscode.postMessage({ command: 'loadChatSessions', roleType: 'backEndDev' }); } } }); }); // 添加团队聊天发送按钮和输入框事件监听器 const teamSendButton = document.getElementById('team-send'); const teamInput = document.getElementById('team-input'); if (teamSendButton && teamInput) { // 发送按钮点击事件 addManagedEventListener(teamSendButton, 'click', function () { sendTeamMessage(); }); // 输入框回车事件 addManagedEventListener(teamInput, 'keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendTeamMessage(); } }); // 输入框自动调整高度 addManagedEventListener(teamInput, 'input', function () { this.style.height = 'auto'; this.style.height = Math.min(this.scrollHeight, 120) + 'px'; }); } // 添加清空聊天按钮事件监听器 const clearButtons = document.querySelectorAll('.chat-clear-button'); clearButtons.forEach((button, index) => { const roleType = button.getAttribute('data-role'); addManagedEventListener(button, 'click', function (e) { const roleType = this.getAttribute('data-role'); if (roleType) { clearChatHistory(roleType); } else { console.error('🗑️ [事件触发] 🔍 未获取到角色类型'); } }); }); // 添加预览设计稿按钮事件监听器 const previewDesignBtn = document.querySelector('.preview-design-btn'); if (previewDesignBtn) { addManagedEventListener(previewDesignBtn, 'click', function (event) { event.preventDefault(); handlePreviewDesignClick(); }); } // 添加事件委托来处理聊天链接点击事件 addManagedEventListener(document, 'click', function (e) { if (e.target.classList.contains('chat-link')) { e.preventDefault(); const roleType = e.target.getAttribute('data-role'); // 优先使用 data-link-url,如果没有则使用 data-link-text const linkText = e.target.getAttribute('data-link-url') || e.target.getAttribute('data-link-text'); if (roleType && linkText) { clickChatLink(roleType, linkText); } else { console.warn('🔗 [事件委托] 缺少必要的属性:', { roleType, linkText }); } } }); // 通知扩展webview已准备就绪 vscode.postMessage({ command: 'webviewReady' }); }); // 监听来自扩展的转发消息 addManagedEventListener(window, 'message', event => { const message = event.data; if (message && message.type) { switch (message.type) { case 'initProjectPageList': // 在这里处理项目打开的逻辑 // 比如高亮显示对应的项目、更新UI状态等 handleInitProjectPageList(message.data); break; case 'chatSessionsLoaded': handleChatSessionsLoaded(message.data); break; case 'teamChatSessionsLoaded': handleTeamChatSessionsLoaded(message.data); break; case 'projectStatusUpdate': handleProjectStatusUpdate(message.data.isProjectExists); break; case 'updateDesignPreviewVisibility': updateDesignPreviewVisibility(message.data.visible); break; case 'productItems': handleProductItemsLoaded(message.data); break; case 'designItems': handleDesignItemsLoaded(message.data); break; case 'frontendDevItems': handleFrontendDevItemsLoaded(message.data); break; case 'backendDevItems': handleBackendDevItemsLoaded(message.data); break; case 'syncButtonComplete': // 现在消息包含syncPageEnName信息 const syncPageEnName = message.data?.syncPageEnName; if (syncPageEnName) { switchSyncButtonToNormal(syncPageEnName); } break; case 'syncLogicComplete': const logicPageEnName = message.data?.pageNameEn; if (logicPageEnName) { switchSyncLogicButtonToNormal(logicPageEnName); // 显示成功提示 vscode.postMessage({ command: 'showMessage', message: '逻辑同步完成!', type: 'info' }); } break; default: } } else { console.warn('📨 [消息监听] [前端] ⚠️ 收到无效消息:', message); } }); // 处理openProject转发消息的函数 function handleInitProjectPageList(projectData) { if (projectData) { const projectName = projectData.projectName || projectData.name; if (projectName) { // 更新页面列表标题 updatePageListHeader(projectName); } else { console.error('❌ [ByteFun Sidebar] 项目数据中没有项目名称'); } // 解析并生成页面列表 if (projectData.projectPageList) { try { const pageList = typeof projectData.projectPageList === 'string' ? JSON.parse(projectData.projectPageList) : projectData.projectPageList; if (Array.isArray(pageList) && pageList.length > 0) { generatePageList(pageList); // 发送消息给sidebarProvider,要求创建页面TS文件 vscode.postMessage({ command: 'tryToCreatePageTSFile', projectData: projectData }); } else { showEmptyPageList(); } } catch (error) { console.error('❌ [ByteFun Sidebar] 解析页面列表失败:', error); showEmptyPageList(); } } else { showEmptyPageList(); } } else { console.error('❌ [ByteFun Sidebar] 项目数据为空'); } } // 更新页面列表标题 function updatePageListHeader(projectName) { const header = document.querySelector('.page-list-header-title'); if (header) { const newTitle = projectName + ' - 页面列表'; header.textContent = newTitle; } else { console.error('❌ [ByteFun Sidebar] 找不到页面列表标题元素'); } } // 生成页面列表 function generatePageList(pageList) { const pageListContainer = document.querySelector('.page-list'); if (!pageListContainer) { console.error('❌ [ByteFun Sidebar] 找不到页面列表容器'); return; } // 清空现有内容 pageListContainer.innerHTML = ''; // 递归生成树形页面项 pageList.forEach((page, index) => { renderPageTree(page, pageListContainer, 0); }); } // 递归渲染页面树 function renderPageTree(page, container, level) { const pageItem = createPageItem(page, level); container.appendChild(pageItem); // 如果是文件夹且有子项,创建子项容器 if (page.resourceId === 0 && page.children && page.children.length > 0) { const childrenContainer = document.createElement('div'); childrenContainer.className = 'page-children-container'; childrenContainer.setAttribute('data-parent-id', page.nodeId); childrenContainer.style.display = 'none'; // 默认折叠 // 递归渲染子项 page.children.forEach(child => { renderPageTree(child, childrenContainer, level + 1); }); container.appendChild(childrenContainer); } } // 创建页面项元素 function createPageItem(page, level = 0) { const pageItem = document.createElement('div'); // 判断是文件夹还是文件 const isFolder = page.resourceId === 0; const hasChildren = isFolder && page.children && page.children.length > 0; pageItem.className = isFolder ? 'page-item folder-item' : 'page-item file-item'; pageItem.setAttribute('data-page-id', page.nodeId || page.id); pageItem.setAttribute('data-level', level); // 设置缩进 pageItem.style.paddingLeft = `${level * 20 + 8}px`; // 格式化时间 const updateTime = formatTime(page.nodeUpdateTime || page.resourceUpdateTime); // 获取页面类型显示名称 const pageTypeName = getPageTypeName(page.type); // 构建HTML内容 let expandIcon = ''; if (isFolder) { expandIcon = hasChildren ? `<span class="expand-icon" data-expanded="false">▶</span>` : `<span class="expand-icon empty"> </span>`; } // 获取合适的图标 let iconContent = ''; if (isFolder) { iconContent = `<span class="folder-icon">📁</span>`; } else { const iconSrc = page.thumbnail || getDefaultPageIcon(page.type); iconContent = `<img src="${iconSrc}" alt="${page.name}">`; } // 构建文件夹名称(包含子项计数) let displayName = page.name; if (isFolder && page.children && page.children.length > 0) { displayName = `${page.name} (${page.children.length})`; } pageItem.innerHTML = ` <div class="page-item-header"> ${expandIcon} <div class="page-icon"> ${iconContent} </div> <div class="page-content"> <div class="page-title-row"> <div class="page-title">${displayName}</div> <div class="page-time">${updateTime}</div> </div> ${!isFolder ? ` <div class="page-meta"> <div class="page-author"> <svg class="page-author-icon" viewBox="0 0 16 16" fill="currentColor"> <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/> </svg> ${page.authorName || page.resourceAuthorName || 'Unknown'} </div> <div class="page-type">${pageTypeName}</div> </div> ` : ''} </div> ${!isFolder ? `<button class="page-sync-button" title="同步代码" data-page-id="${page.nodeId || page.id}">同步代码</button>` : ''} </div> `; // 为文件夹添加展开/折叠事件 if (isFolder && hasChildren) { const expandIcon = pageItem.querySelector('.expand-icon'); const header = pageItem.querySelector('.page-item-header'); addManagedEventListener(header, 'click', (e) => { e.stopPropagation(); toggleFolderExpansion(page.nodeId, expandIcon); }); // 添加样式,使文件夹可点击 header.style.cursor = 'pointer'; } else if (!isFolder) { // 为文件添加点击事件 const header = pageItem.querySelector('.page-item-header'); addManagedEventListener(header, 'click', () => openProjectPage(page)); } // 为文件的图片添加错误处理 if (!isFolder) { const img = pageItem.querySelector('img'); if (img) { addManagedEventListener(img, 'error', function () { this.src = getDefaultPageIcon(page.type); }); } // 为同步按钮添加事件监听器 const syncButton = pageItem.querySelector('.page-sync-button'); if (syncButton) { addManagedEventListener(syncButton, 'click', function (e) { e.stopPropagation(); // 阻止事件冒泡,避免触发页面点击事件 handlePageSyncClick(page); }); } } return pageItem; } // 切换文件夹展开/折叠状态 function toggleFolderExpansion(folderId, expandIcon) { const childrenContainer = document.querySelector(`[data-parent-id="${folderId}"]`); if (childrenContainer) { const isExpanded = expandIcon.getAttribute('data-expanded') === 'true'; if (isExpanded) { // 折叠 childrenContainer.style.display = 'none'; expandIcon.textContent = '▶'; expandIcon.setAttribute('data-expanded', 'false'); } else { // 展开 childrenContainer.style.display = 'block'; expandIcon.textContent = '▼'; expandIcon.setAttribute('data-expanded', 'true'); } } } // 获取页面类型名称 function getPageTypeName(type) { const typeMap = { 1: '文件夹', 2: '页面', 3: '组件', 4: '模板', 5: '子模块', 6: '公共页面' }; return typeMap[type] || '页面'; } // 获取默认页面图标 function getDefaultPageIcon(type) { const iconMap = { 1: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iIzAwNzBmMyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0LjUgMTMuNWgtMTNBLjUuNSAwIDAxMSAxM1YzYS41LjUgMCAwMS41LS41SDZsMS0xaDdhLjUuNSAwIDAxLjUuNXY5YS41LjUgMCAwMS0uNS41eiIvPgo8L3N2Zz4K', // 文件夹 2: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iIzAwNzBmMyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIgMmEyIDIgMCAwMC0yIDJ2OGEyIDIgMCAwMDIgMmgxMmEyIDIgMCAwMDItMlY0YTIgMiAwIDAwLTItMkgyem0wIDJoMTJ2OEgyVjR6Ii8+Cjwvc3ZnPgo=', // 页面 3: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iIzAwNzBmMyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTggMGE4IDggMCAxMDAgMTZBOCA4IDAgMDA4IDB6TTcgM2EuNS41IDAgMDEuNS0uNWguNWEuNS41IDAgMDEuNS41djJhLjUuNSAwIDAxLS41LjVoLS41YS41LjUgMCAwMS0uNS0uNVYzem0wIDVhLjUuNSAwIDAxLjUtLjVoLjVhLjUuNSAwIDAxLjUuNXY0YS41LjUgMCAwMS0uNS41aC0uNWEuNS41IDAgMDEtLjUtLjV6bTAgMmEuNS41IDAgMDEuNS0uNWgzYS41LjUgMCAwMTAgMWgtNWEuNS41IDAgMDEtLjUtLjV6Ii8+Cjwvc3ZnPgo=', // 组件 4: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iIzAwNzBmMyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTMgMmEyIDIgMCAwMC0yIDJ2OGEyIDIgMCAwMDIgMmgxMGEyIDIgMCAwMDItMlY0YTIgMiAwIDAwLTItMkgzem0wIDJoMTB2OEgzVjR6bTIgMmEuNS41IDAgMDEuNS0uNWg1YS41LjUgMCAwMTAgMWgtNWEuNS41IDAgMDEtLjUtLjV6bTAgMmEuNS41IDAgMDEuNS0uNWgzYS41LjUgMCAwMTAgMWgtM2EuNS41IDAgMDEtLjUtLjV6Ii8+Cjwvc3ZnPgo=', // 模板 5: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iIzAwNzBmMyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTggMGE4IDggMCAxMDAgMTZBOCA4IDAgMDA4IDB6bTMuNSA2TDggOS41IDQuNSA2aDd6Ii8+Cjwvc3ZnPgo=', // 子模块 6: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iIzAwNzBmMyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTggMGE4IDggMCAxMDAgMTZBOCA4IDAgMDA4IDB6bTAgMTRhNiA2IDAgMTEwLTEyIDYgNiAwIDAxMCAxMnoiLz4KPC9zdmc+Cg==' // 公共页面 }; return iconMap[type] || iconMap[2]; // 默认使用页面图标 } // 格式化时间 function formatTime(timestamp) { if (!timestamp) return '未知时间'; try { const date = new Date(timestamp); const now = new Date(); const diff = now.getTime() - date.getTime(); // 计算时间差 const minutes = Math.floor(diff / (1000 * 60)); const hours = Math.floor(diff / (1000 * 60 * 60)); const days = Math.floor(diff / (1000 * 60 * 60 * 24)); if (minutes < 1) { return '刚刚'; } else if (minutes < 60) { return minutes + '分钟前'; } else if (hours < 24) { return hours + '小时前'; } else if (days < 7) { return days + '天前'; } else { // 超过一周显示具体日期 return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' }); } } catch (error) { console.error('格式化时间失败:', error); return '时间错误'; } } // 显示空页面列表 function showEmptyPageList() { const pageListContainer = document.querySelector('.page-list'); if (pageListContainer) { pageListContainer.innerHTML = ` <div class="empty-state"> <div class="empty-state-icon">📁</div> <div>从我的项目打开vscode工程</div> </div> `; } else { console.error('❌ [ByteFun Sidebar] 找不到页面列表容器'); } } function selectPage(pageId) { // 移除所有页面的选中状态 const allPages = document.querySelectorAll('.page-item'); allPages.forEach(page => { page.classList.remove('active'); }); // 为指定页面添加选中状态 const targetPage = document.querySelector('[data-page-id="' + pageId + '"]'); if (targetPage) { targetPage.classList.add('active'); state.selectedPageId = pageId; vscode.setState(state); } } function setActiveMenu(menuId) { // 移除所有菜单项的激活状态 const allMenuItems = document.querySelectorAll('.menu-item'); allMenuItems.forEach(item => { item.classList.remove('active'); }); // 为指定菜单项添加激活状态 if (menuId === 'project') { allMenuItems[0].classList.add('active'); } else if (menuId === 'library') { allMenuItems[1].classList.add('active'); } state.activeMenuId = menuId; vscode.setState(state); } function handleProjectClick() { setActiveMenu('project'); vscode.postMessage({ command: 'openProjects' }); } function handleLibraryClick() { setActiveMenu('library'); vscode.postMessage({ command: 'openLibrary' }); } function handleRefreshClick() { vscode.postMessage({ command: 'refresh' }); } function handleSyncAllClick() { vscode.postMessage({ command: 'syncAllPages' }); } function handlePageSyncClick(pageData) { vscode.postMessage({ command: 'syncPage', pageData: pageData }); } function openProjectPage(page) { selectPage(page.nodeId || page.id); vscode.postMessage({ command: 'openProjectPage', pageData: page }); } // Tab 切换功能 function switchTab(tabId) { // 获取所有tab项和面板 const tabItems = document.querySelectorAll('.tab-item'); const tabPanels = document.querySelectorAll('.tab-panel'); // 移除所有active状态 tabItems.forEach(item => { item.classList.remove('active'); }); tabPanels.forEach(panel => { panel.classList.remove('active'); }); // 激活选中的tab const selectedTabItem = document.querySelector(`[data-tab="${tabId}"]`); const selectedTabPanel = document.querySelector(`[data-panel="${tabId}"]`); if (selectedTabItem) { selectedTabItem.classList.add('active'); } if (selectedTabPanel) { selectedTabPanel.classList.add('active'); } // 保存状态 state.activeTab = tabId; vscode.setState(state); // 如果是聊天角色tab,加载聊天记录 if (tabId === 'product') { // 产品角色使用特殊的item加载方式 loadProductItems(); } else if (tabId === 'design') { // 设计角色使用特殊的item加载方式 loadDesignItems(); } else if (tabId === 'frontEndDev') { // 前端开发角色使用特殊的item加载方式 loadFrontendDevItems(); } else if (tabId === 'backEndDev') { // 后端开发角色使用特殊的item加载方式 loadBackendDevItems(); } else if (['operations'].includes(tabId)) { // 其他角色使用聊天方式 loadChatMessages(tabId); } else if (tabId === 'team') { loadTeamChatMessages(); } } // 加载聊天消息 function loadChatMessages(roleType) { vscode.postMessage({ command: 'loadChatSessions', roleType: roleType }); } // 加载产品项目 function loadProductItems() { vscode.postMessage({ command: 'loadChatSessions', roleType: 'product' }); } // 加载设计项目 function loadDesignItems() { vscode.postMessage({ command: 'loadChatSessions', roleType: 'design' }); } // 加载前端开发项目(用于前端开发tab) function loadFrontendDevItems() { vscode.postMessage({ command: 'loadChatSessions', roleType: 'frontEndDev' }); } // 加载后端开发项目(用于后端开发tab) function loadBackendDevItems() { vscode.postMessage({ command: 'loadChatSessions', roleType: 'backEndDev' }); } // 发送聊天消息 function sendChatMessage(roleType) { const input = document.getElementById(`${roleType}-input`); const sendButton = document.getElementById(`${roleType}-send`); if (!input || !sendButton) { return; } const content = input.value.trim(); if (!content) { return; } // 禁用输入框和按钮 input.disabled = true; sendButton.disabled = true; sendButton.textContent = '发送中...'; // 获取当前会话(如果没有则会自动创建) vscode.postMessage({ command: 'sendMessage', roleType: roleType, content: content }); // 清空输入框 input.value = ''; input.style.height = 'auto'; } // 渲染聊天消息 function renderChatMessages(roleType, sessions) { const messagesContainer = document.getElementById(`${roleType}-messages`); if (!messagesContainer) { return; } // 清空现有内容 messagesContainer.innerHTML = ''; if (!sessions || sessions.length === 0) { // 创建默认对话并显示自我介绍 createDefaultChatSession(roleType); return; } // 找到最新的会话(第一个) const latestSession = sessions[0]; if (!latestSession || !latestSession.messages || latestSession.messages.length === 0) { // 如果会话为空,添加自我介绍 addIntroductionMessage(roleType); return; } // 渲染会话中的所有消息 latestSession.messages.forEach((message, index) => { const messageElement = createChatMessageElement(message); messagesContainer.appendChild(messageElement); }); // 滚动到底部 messagesContainer.scrollTop = messagesContainer.scrollHeight; // 重新启用输入框 resetChatInput(roleType); } // 创建聊天消息元素 function createChatMessageElement(message) { const messageElement = document.createElement('div'); messageElement.className = `chat-message ${message.role}`; // 创建头像元素 const avatar = document.createElement('div'); avatar.className = `chat-avatar ${message.role}`; if (message.role === 'user') { avatar.className += ' user'; avatar.textContent = '我'; } else { // 为AI助手添加角色特定的头像和样式 avatar.className += ` assistant ${message.roleType}`; const roleAvatars = { product: '📊', design: '🎨', frontEndDev: '💻', backEndDev: '🧪', operations: '📈' }; avatar.textContent = roleAvatars[message.roleType] || '🤖'; } // 创建消息包装器 const messageWrapper = document.createElement('div'); messageWrapper.className = 'chat-message-wrapper'; // 创建消息内容 const content = document.createElement('div'); content.className = 'chat-message-content'; // 解析并渲染带链接的内容 const processedContent = parseChatLinks(message.content, message.roleType, message); content.innerHTML = processedContent; // 创建时间戳(如果有的话) if (message.timestamp) { const timeElement = document.createElement('div'); timeElement.className = 'chat-message-time'; timeElement.textContent = formatChatTime(message.timestamp); messageWrapper.appendChild(content); messageWrapper.appendChild(timeElement); } else { messageWrapper.appendChild(content); } // 组装消息元素 messageElement.appendChild(avatar); messageElement.appendChild(messageWrapper); return messageElement; } // 创建默认聊天会话 function createDefaultChatSession(roleType) { vscode.postMessage({ command: 'createChatSession', roleType: roleType }); } // 添加自我介绍消息 function addIntroductionMessage(roleType) { const messagesContainer = document.getElementById(`${roleType}-messages`); if (!messagesContainer) { return; } const introductions = { product: '👋 你好!我是产品经理,我可以帮助你进行需求分析、产品规划、用户体验设计等工作。', design: '👋 你好!我是UI设计师,我可以帮助你进行UI/UX设计、视觉设计、交互设计等工作。', frontEndDev: '👋 你好!我是前端开发工程师,我可以帮助你解决前端的技术问题、代码实现、架构设计等问题。', backEndDev: '👋 你好!我是后端开发工程师,我可以帮助你解决后端的技术问题、代码实现、架构设计等问题。', test: '👋 你好!我是测试工程师,我可以帮助你进行测试用例设计、质量保证、Bug分析等工作。', operations: '👋 你好!我是运营专员,我可以帮助你进行用户运营、数据分析、活动策划等工作。' }; const introMessage = { role: 'assistant', content: introductions[roleType] || '👋 你好!我可以帮助你解决各种问题。', roleType: roleType, timestamp: Date.now() }; const messageElement = createChatMessageElement(introMessage); messagesContainer.appendChild(messageElement); // 滚动到底部 messagesContainer.scrollTop = messagesContainer.scrollHeight; } // 重置聊天输入框状态 function resetChatInput(roleType) { const input = document.getElementById(`${roleType}-input`); const sendButton = document.getElementById(`${roleType}-send`); if (input) { input.disabled = false; } if (sendButton) { sendButton.disabled = false; sendButton.textContent = '发送'; } } // 格式化聊天时间 function formatChatTime(timestamp) { if (!timestamp) return ''; try { const date = new Date(timestamp); const now = new Date(); const diff = now.getTime() - date.getTime(); // 计算时间差 const minutes = Math.floor(diff / (1000 * 60)); const hours = Math.floor(diff / (1000 * 60 * 60)); const days = Math.floor(diff / (1000 * 60 * 60 * 24)); if (minutes < 1) { return '刚刚'; } else if (minutes < 60) { return minutes + 'm'; } else if (hours < 24) { return hours + 'h'; } else if (days < 7) { return days + 'd'; } else { // 超过一周显示具体日期 return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' }); } } catch (error) { console.error('格式化聊天时间失败:', error); return ''; } } // 处理聊天会话加载完成 function handleChatSessionsLoaded(data) { // 特殊处理产品角色和设计角色 - 已经改为使用items消息类型 if (data && data.roleType === 'product') { return; } if (data && data.roleType === 'design') { return; } // 特殊处理前端开发和后端开发角色 - 已经改为使用items消息类型 if (data && data.roleType === 'frontEndDev') { return; } if (data && data.roleType === 'backEndDev') { return; } if (data && data.roleType && Array.isArray(data.sessions)) { renderChatMessages(data.roleType, data.sessions); } else { console.error('🔄 [流程-2] [前端] ❌ 聊天消息数据格式错误:', data); } } // 处理产品项目加载完成 function handleProductItemsLoaded(items) { if (Array.isArray(items)) { renderProductItems(items); } else { console.error('📊 [产品角色] ❌ 产品项目数据格式错误:', items); renderProductItems([]); } } // 渲染产品项目列表 function renderProductItems(items) { const itemsContainer = document.getElementById('product-items'); if (!itemsContainer) { console.error('📊 [产品角色] ❌ 未找到产品项目容器'); return; } // 清空现有内容 itemsContainer.innerHTML = ''; if (!items || items.length === 0) { // 显示空状态 const emptyState = document.createElement('div'); emptyState.className = 'empty-product-state'; emptyState.innerHTML = ` <div class="empty-product-state-icon">📋</div> <div>暂无产品文档</div> <div style="font-size: 12px; margin-top: 8px; opacity: 0.7;"> 请在AI输入框输入发送:设计产品需求。 </div> `; itemsContainer.appendChild(emptyState); return; } // 渲染每个项目 items.forEach(item => { const itemElement = createProductItemElement(item); itemsContainer.appendChild(itemElement); }); } // 创建产品项目元素 function createProductItemElement(item) { const itemElement = document.createElement('div'); itemElement.className = 'product-item'; // 为业务逻辑item添加特殊类名以应用不同的高度 if (item.type === 'business_logic') { itemElement.classList.add('business-logic'); } // 创建操作按钮容器 const actions = document.createElement('div'); actions.className = 'product-item-actions'; // // 只在业务逻辑设计item中显示编辑按钮 // if (item.type === 'business_logic') { // // 创建编辑按钮 // const editButton = document.createElement('button'); // editButton.className = 'edit-button icon-button'; // editButton.title = '编辑文档'; // 添加hover提示 // // 创建图标img元素 // const editIcon = document.createElement('img'); // editIcon.src = window.ICON_URIS.editIcon; // editIcon.className = 'button-icon'; // editIcon.alt = '编辑'; // editButton.appendChild(editIcon); // editButton.addEventListener('click', function (e) { // e.stopPropagation(); // 阻止事件冒泡,避免触发item点击事件 // handleProductItemEdit(item); // }); // actions.appendChild(editButton); // } // 为整个item添加点击事件和cursor样式 itemElement.style.cursor = 'pointer'; itemElement.addEventListener('click', function () { handleProductItemClick(item); }); // 区分两种不同的布局 if (item.type === 'business_logic') { // 业务逻辑设计item:一行布局,无左侧图标 const contentArea = document.createElement('div'); contentArea.style.display = 'flex'; contentArea.style.alignItems = 'center'; contentArea.style.flex = '1'; // 只创建标题,状态图标已在后端合并到title中 const title = document.createElement('div'); title.className = 'product-item-title'; title.textContent = item.title; contentArea.appendChild(title); itemElement.appendChild(contentArea); } else { // 产品需求文档item:保持原有布局(图标+内容) const icon = document.createElement('div'); icon.className = 'product-item-icon'; // 使用PNG图标替换emoji const iconImg = document.createElement('img'); iconImg.src = window.ICON_URIS.productIcon; iconImg.className = 'item-icon-img'; iconImg.alt = '产品需求文档'; icon.appendChild(iconImg); const content = document.createElement('div'); content.className = 'product-item-content'; const title = document.createElement('div'); title.className = 'product-item-title'; title.textContent = item.title; const subtitle = document.createElement('div'); subtitle.className = 'product-item-subtitle'; subtitle.textContent = '产品需求文档'; content.appendChild(title); content.appendChild(subtitle); const contentArea = document.createElement('div'); contentArea.style.display = 'flex'; contentArea.style.alignItems = 'center'; contentArea.style.flex = '1'; contentArea.appendChild(icon); contentArea.appendChild(content); itemElement.appendChild(contentArea); } itemElement.appendChild(actions); return itemElement; } // 处理产品项目点击事件(预览) function handleProductItemClick(item) { if (item.type === 'document' && item.path) { // 打开产品需求设计规范预览 vscode.postMessage({ command: 'openPrdDesignSpec' }); } else if (item.type === 'business_logic' && item.moduleData) { // 处理业务逻辑设计点击预览 vscode.postMessage({ command: 'openBusinessLogicModule', moduleData: item.moduleData }); } } // 处理产品项目编辑事件 function handleProductItemEdit(item) { if (item.type === 'document') { // 编辑产品需求文档 vscode.postMessage({ command: 'editProductDocument', item: item }); } else if (item.type === 'business_logic' && item.moduleData) { // 编辑业务逻辑设计文档 vscode.postMessage({ command: 'editBusinessLogicModule', moduleData: item.moduleData }); } } // 处理设计项目加载完成 function handleDesignItemsLoaded(items) { if (Array.isArray(items)) { renderDesignItems(items); } else { console.error('🎨 [设计角色] ❌ 设计项目数据格式错误:', items); renderDesignItems([]); } } // 处理前端开发项目数据加载完成 function handleFrontendDevItemsLoaded(data) { const items = data?.items || []; if (Array.isArray(items)) { renderFrontendDevItems(items); } else { console.error('💻 [前端开发角色] ❌ 前端开发项目数据格式错误:', data); renderFrontendDevItems([]); } } // 处理后端开发项目数据加载完成 function handleBackendDevItemsLoaded(data) { const items = data?.items || []; if (Array.isArray(items)) { renderBackendDevItems(items); } else { console.error('🔧 [后端开发角色] ❌ 后端开发项目数据格式错误:', data); renderBackendDevItems([]); } } // 渲染设计项目列表 function renderDesignItems(items) { const itemsContainer = document.getElementById('design-messages'); if (!itemsContainer) { console.error('🎨 [设计角色] ❌ 未找到设计项目容器'); return; } // 清空现有内容 itemsContainer.innerHTML = ''; if (!items || items.length === 0) { // 显示空状态 const emptyState = document.createElement('div'); emptyState.className = 'empty-product-state'; emptyState.innerHTML = ` <div class="empty-product-state-icon">🎨</div> <div>暂无UI设计稿</div> <div style="font-size: 12px; margin-top: 8px; opacity: 0.7;"> AI输入发送:设计UI规范。完成后审核UI设计规范,并开始AI设计UI界面。 </div> `; itemsContainer.appendChild(emptyState); return; } // 渲染每个项目 items.forEach(item => { const itemElement = createDesignItemElement(item); itemsContainer.appendChild(itemElement); }); } // 创建设计项目元素 function createDesignItemElement(item) { const itemElement = document.createElement('div'); itemElement.className = 'product-item'; // 为页面UI设计item添加特殊类名 if (item.type === 'page_ui_design') { itemElement.classList.add('business-logic'); // 设置页面ID和页面英文名用于后续查找 if (item.pageData && item.pageData.id) { itemElement.setAttribute('data-page-id', item.pageData.id); } if (item.pageData && item.pageData.pageNameEN) { itemElement.setAttribute('data-page-en-name', item.pageData.pageNameEN); } } // 为整个item添加点击事件和cursor样式 itemElement.style.cursor = 'pointer'; itemElement.addEventListener('click', function () { handleDesignItemClick(item); }); // 创建操作按钮容器 const actions = document.createElement('div'); actions.className = 'product-item-actions'; // 只有页面UI设计item才显示按钮组 if (item.type === 'page_ui_design') { // 创建同步按钮 const syncButton = document.createElement('button'); syncButton.className = 'edit-button icon-button'; syncButton.title = '同步UI部分'; // 添加hover提示 // 创建图标img元素 const synIcon = document.createElement('img'); synIcon.src = window.ICON_URIS.synIcon; synIcon.className = 'button-icon'; synIcon.alt = '同步UI部分'; // 根据status状态添加颜色类(status在pageData中) if (item.pageData && item.pageData.status === 'completed') { synIcon.classList.add('syn-icon-completed'); } syncButton.appendChild(synIcon); syncButton.addEventListener('click', function (e) { e.stopPropagation(); // 阻止事件冒泡,避免触发item点击事件 handleUIDesignSync(item); }); actions.appendChild(syncButton); // 创建设计思路按钮 const thinkingButton = document.createElement('button'); thinkingButton.className = 'edit-button icon-button'; thinkingButton.title = '设计思考'; // 添加hover提示 // 创建图标img元素 const thinkIcon = document.createElement('img'); thinkIcon.src = window.ICON_URIS.thinkIcon; thinkIcon.className = 'button-icon'; thinkIcon.alt = '思路'; // 根据status状态添加颜色类(status在pageData中) if (item.pageData && item.pageData.status === 'completed') { thinkIcon.classList.add('think-icon-completed'); } thinkingButton.appendChild(thinkIcon); thinkingButton.addEventListener('click', function (e) { e.stopPropagation(); // 阻止事件冒泡,避免触发item点击事件 handleUIDesignThinking(item); }); // 创建编辑按钮 const editButton = document.createElement('button'); editButton.className = 'edit-button icon-button'; editButton.title = '编辑UI'; // 添加hover提示 // 创建图标img元素 const editIcon = document.createElement('img'); editIcon.src = window.ICON_URIS.editIcon; editIcon.className = 'button-icon'; editIcon.alt = '编辑'; editButton.appendChild(editIcon); editButton.addEventListener('click', function (e) { e.stopPropagation(); // 阻止事件冒泡,避免触发item点击事件 handleDesignItemEdit(item); }); actions.appendChild(thinkingButton); actions.appendChild(editButton); } // 区分两种不同的布局 if (item.type === 'page_ui_design') { // 页面UI设计item:一行布局,无左侧图标 const contentArea = document.createElement('div'); contentArea.style.display = 'flex'; contentArea.style.alignItems = 'center'; contentArea.style.flex = '1'; // 只创建标题,状态图标已在后端合并到title中 const title = document.createElement('div'); title.className = 'product-item-title'; title.textContent = item.title; contentArea.appendChild(title); itemElement.appendChild(contentArea); } else { // 预览设计稿item:保持原有布局(图标+内容) const icon = document.createElement('div'); icon.className = 'product-item-icon'; // 使用PNG图标替换emoji const iconImg = document.createElement('img'); iconImg.src = window.ICON_URIS.uiDesignIcon; iconImg.className = 'item-icon-img'; iconImg.alt = '设计稿预览'; icon.appendChild(iconImg); const content = document.createElement('div'); content.className = 'product-item-content'; const title = document.createElement('div'); title.className = 'product-item-title'; title.textContent = item.title; const subtitle = document.createElement('div'); subtitle.className = 'product-item-subtitle clickable'; subtitle.textContent = item.subtitle || '查看设计规范'; // 为副标题添加点击事件 subtitle.addEventListener('click', function (e) { e.stopPropagation(); // 阻止事件冒泡 handleDesignSpecClick(); }); content.appendChild(title); content.appendChild(subtitle); const contentArea = document.createElement('div'); contentArea.style.display = 'flex'; contentArea.style.alignItems = 'center'; contentArea.style.flex = '1'; contentArea.appendChild(icon); contentArea.appendChild(content); itemElement.appendChild(contentArea); } // 只有有按钮的item才添加actions容器 if (actions.children.length > 0) { itemElement.appendChild(actions); } return itemElement; } // 渲染前端开发项目列表 function renderFrontendDevItems(items) { const itemsContainer = document.getElementById('frontEndDev-messages'); if (!itemsContainer) { console.error('💻 [前端开发角色] ❌ 未找到前端开发项目容器'); return; } // 清空现有内容 itemsContainer.innerHTML = ''; if (items.length === 0) { itemsContainer.innerHTML = '<div class="empty-state">暂无前端开发项目</div>'; return; } // 渲染每个项目 items.forEach(item => { const itemElement = createFrontendDevItemElement(item); itemsContainer.appendChild(itemElement); }); } // 渲染后端开发项目列表 function renderBackendDevItems(items) { const itemsContainer = document.getElementById('backEndDev-messages'); if (!itemsContainer) { console.error('🔧 [后端开发角色] ❌ 未找到后端开发项目容器'); return; } // 清空现有内容 itemsContainer.innerHTML = ''; if (items.length === 0) { itemsContainer.innerHTML = '<div class="empty-state">暂无后端开发项目</div>'; return; } // 渲染每个项目 items.forEach(item => { const itemElement = createBackendDevItemElement(item); itemsContainer.appendChild(itemElement); }); } // 创建前端开发项目元素 function createFrontendDevItemElement(item) { const itemElement = document.createElement('div'); itemElement.className = 'product-item business-logic'; // 设置页面ID和页面英文名用于后续查找(参考UI设计实现) if (item.pageData && item.pageData.id) { itemElement.setAttribute('data-page-id', item.pageData.id); } if (item.pageData && item.pageData.pageNameEN) { itemElement.setAttribute('data-page-en-name', item.pageData.pageNameEN); } // 为整个item添加点击事件和cursor样式 itemElement.style.cursor = 'pointer'; itemElement.addEventListener('click', function () { handleFrontendDevItemClick(item); }); // 创建操作按钮容器 const actions = document.createElement('div'); actions.className = 'product-item-actions'; // 创建同步逻辑部分按钮 const syncLogicButton = document.createElement('button'); syncLogicButton.className = 'edit-button icon-button'; syncLogicButton.title = '同步逻辑部分'; // 添加hover提示 // 创建同步图标img元素 const syncIcon = document.createElement('img'); syncIcon.src = window.ICON_URIS.synIcon; syncIcon.className = 'button-icon'; syncIcon.alt = '同步逻辑部分'; // 根据codeStatus状态添加颜色类(与思考设计完成状态颜色一样) if (item.pageData && item.pageData.codeStatus === 'completed') { syncIcon.classList.add('syn-icon-completed'); } syncLogicButton.appendChild(syncIcon); syncLogicButton.addEventListener('click', function (e) { e.stopPropagation(); // 阻止事件冒泡,避免触发item点击事件 handleSyncLogicClick(item); }); actions.appendChild(syncLogicButton); // 创建代码设计按钮 const codeDesignButton = document.createElement('button'); codeDesignButton.className = 'edit-button icon-button'; codeDesignButton.title = '代码设计'; // 添加hover提示 // 创建图标img元素 const thinkIcon = document.createElement('img'); thinkIcon.src = window.ICON_URIS.thinkIcon; thinkIcon.className = 'button-icon'; thinkIcon.alt = '代码设计'; // 根据thinkStatus状态添加颜色类(thinkStatus在pageData中) if (item.pageData && item.pageData.thinkStatus === 'completed') { thinkIcon.classList.add('think-icon-completed'); } codeDesignButton.appendChild(thinkIcon); codeDesignButton.addEventListener('click', function (e) { e.stopPropagation(); // 阻止事件冒泡,避免触发item点击事件 handleCodeDesignClick(item); }); actions.appendChild(codeDesignButton); // 前端开发item:一行布局,无左侧图标 const contentArea = document.createElement('div'); contentArea.style.display = 'flex'; contentArea.style.alignItems = 'center'; contentArea.style.flex = '1'; // 只创建标题,状态图标已在后端合并到title中 const title = document.createElement('div'); title.className = 'product-item-title'; title.textContent = item.title; contentArea.appendChild(title); itemElement.appendChild(contentArea); itemElement.appendChild(actions); return itemElement; } // 创建后端开发项目元素 function createBackendDevItemElement(item) { const itemElement = document.createElement('div'); itemElement.className = 'product-item'; // 为整个item添加点击事件和cursor样式 itemElement.style.cursor = 'pointer'; itemElement.addEventListener('click', function () { handleBackendDevItemClick(item); }); // 区分两种不同的布局 if (item.type === 'backend_api_design') { // 后端接口设计item:保持原有布局(图标+内容),类似于设计角色的"预览设计稿" const icon = document.createElement('div'); icon.className = 'product-item-icon'; // 使用PNG图标替换emoji const iconImg = document.createElement('img'); iconImg.src = window.ICON_URIS.productIcon; // 可以使用productIcon或者其他合适的图标 iconImg.className = 'item-icon-img'; iconImg.alt = '后端接口设计'; icon.appendChild(iconImg); const content = document.createElement('div'); content.className = 'product-item-content'; const title = document.createElement('div'); title.className = 'product-item-title'; title.textContent = item.title; const subtitle = document.createElement('div'); subtitle.className = 'product-item-subtitle'; subtitle.textContent = item.subtitle || '管理后端API接口'; content.appendChild(title); content.appendChild(subtitle); const contentArea = document.createElement('div'); contentArea.style.display = 'flex'; contentArea.style.alignItems = 'center'; contentArea.style.flex = '1'; contentArea.appendChild(icon); contentArea.appendChild(content); itemElement.appendChild(cont