diff --git a/index.html b/index.html index 9a5d38e..4015727 100644 --- a/index.html +++ b/index.html @@ -28,7 +28,16 @@
- +
+
+
    + +
+
+
+ +
+
diff --git a/src/index.css b/src/index.css index 957ff21..f12c922 100644 --- a/src/index.css +++ b/src/index.css @@ -170,4 +170,89 @@ body { .close-btn:hover { background-color: #2980b9; +} + +/* Tabs styles */ +.tabs-container { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; +} + +.tabs-header { + background-color: #f5f5f5; + border-bottom: 1px solid #ddd; +} + +.tabs-list { + display: flex; + list-style: none; + margin: 0; + padding: 0; + height: 40px; +} + +.tab-item { + display: flex; + align-items: center; + padding: 0 15px; + height: 100%; + border-right: 1px solid #ddd; + background-color: #fff; + cursor: pointer; + user-select: none; + position: relative; +} + +.tab-item.active { + background-color: #fff; + border-bottom: 2px solid #1890ff; +} + +.tab-item .tab-title { + margin-right: 8px; + max-width: 150px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.tab-item .tab-close { + width: 16px; + height: 16px; + line-height: 16px; + text-align: center; + border-radius: 50%; + color: #999; + font-size: 12px; +} + +.tab-item .tab-close:hover { + background-color: #e6e6e6; + color: #666; +} + +.tabs-content { + flex: 1; + position: relative; +} + +.tab-pane { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: none; +} + +.tab-pane.active { + display: block; +} + +.tab-pane webview { + width: 100%; + height: 100%; + border: none; } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ef06b29..b1ac258 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { WebviewTag } from "electron"; +import { ipcRenderer, WebviewTag } from "electron"; // 菜单项 interface MenuItem { @@ -28,6 +28,121 @@ interface ApiResponse { data: T; } +// Tab management +interface Tab { + id: string; + title: string; + url: string; + webview: WebviewTag; +} + +let tabs: Tab[] = []; +let activeTabId: string | null = null; + +// Create a new tab +function createTab(title: string, url: string): Tab { + const id = `tab-${Date.now()}`; + const tabPane = document.createElement('div'); + tabPane.className = 'tab-pane'; + tabPane.id = `pane-${id}`; + + const webview = document.createElement('webview'); + webview.className = 'page-content'; + webview.setAttribute('autosize', 'on'); + webview.setAttribute('allowpopups', 'true'); + webview.setAttribute('webpreferences', 'contextIsolation=yes, nodeIntegration=no'); + webview.src = url; + + tabPane.appendChild(webview); + document.getElementById('tabsContent')?.appendChild(tabPane); + + const tab: Tab = { + id, + title, + url, + webview + }; + + tabs.push(tab); + return tab; +} + +// Create tab header +function createTabHeader(tab: Tab): HTMLLIElement { + const li = document.createElement('li'); + li.className = 'tab-item'; + li.dataset.tabId = tab.id; + + const titleSpan = document.createElement('span'); + titleSpan.className = 'tab-title'; + titleSpan.textContent = tab.title; + + const closeButton = document.createElement('span'); + closeButton.className = 'tab-close'; + closeButton.textContent = '×'; + closeButton.addEventListener('click', (e) => { + e.stopPropagation(); + closeTab(tab.id); + }); + + li.appendChild(titleSpan); + li.appendChild(closeButton); + + li.addEventListener('click', () => { + activateTab(tab.id); + }); + + return li; +} + +// Activate a tab +function activateTab(tabId: string) { + const tab = tabs.find(t => t.id === tabId); + if (!tab) return; + + // Update active states + document.querySelectorAll('.tab-item').forEach(item => { + item.classList.remove('active'); + }); + document.querySelectorAll('.tab-pane').forEach(pane => { + pane.classList.remove('active'); + }); + + // Activate the selected tab + const tabElement = document.querySelector(`.tab-item[data-tab-id="${tabId}"]`); + const paneElement = document.getElementById(`pane-${tabId}`); + if (tabElement && paneElement) { + tabElement.classList.add('active'); + paneElement.classList.add('active'); + } + + activeTabId = tabId; +} + +// Close a tab +function closeTab(tabId: string) { + const tabIndex = tabs.findIndex(t => t.id === tabId); + if (tabIndex === -1) return; + + const tab = tabs[tabIndex]; + const tabElement = document.querySelector(`.tab-item[data-tab-id="${tabId}"]`); + const paneElement = document.getElementById(`pane-${tabId}`); + + if (tabElement) tabElement.remove(); + if (paneElement) paneElement.remove(); + + tabs.splice(tabIndex, 1); + + // If we closed the active tab, activate another one + if (activeTabId === tabId) { + if (tabs.length > 0) { + activateTab(tabs[Math.min(tabIndex, tabs.length - 1)].id); + } else { + activeTabId = null; + } + } +} + // 检查登录状态 function checkLoginStatus() { const cookie = window.electronAPI.getSessionStorage('cookie'); @@ -71,7 +186,7 @@ async function getMenuList(): Promise { } } -// 创建菜单项 +// Modify the createMenuItem function to handle new tab creation function createMenuItem(item: MenuItem, menuList: MenuItem[]): HTMLLIElement { const li = document.createElement('li'); li.className = 'menu-item'; @@ -89,7 +204,7 @@ function createMenuItem(item: MenuItem, menuList: MenuItem[]): HTMLLIElement { if (item.Url) { li.addEventListener('click', async () => { - // 移除其他菜单项的选中状态和图标 + // Remove active state from other menu items document.querySelectorAll('.menu-item').forEach(menuItem => { menuItem.classList.remove('active'); const menuIcon = menuItem.querySelector('.menu-icon') as HTMLImageElement; @@ -101,7 +216,7 @@ function createMenuItem(item: MenuItem, menuList: MenuItem[]): HTMLLIElement { } }); - // 添加当前菜单项的选中状态和图标 + // Add active state to current menu item li.classList.add('active'); icon.src = item.IconConfig._1x.Selected; @@ -111,7 +226,12 @@ function createMenuItem(item: MenuItem, menuList: MenuItem[]): HTMLLIElement { console.log('✅ URL 可访问:', result.status); const cookies: string = window.electronAPI.getSessionStorage('cookie'); await window.electronAPI.setWebviewCookie(url, cookies); - (document.querySelector("webview") as WebviewTag).src = url; + + // Create new tab + const tab = createTab(item.ShowName, url); + const tabHeader = createTabHeader(tab); + document.getElementById('tabsList')?.appendChild(tabHeader); + activateTab(tab.id); } else { console.warn('❌ URL 不可访问:', result.error ?? `status ${result.status}`); showErrorModal(`无法访问 ${url}\r\n异常原因:${result.error ?? `status ${result.status}`}\r\n请联系10000技术支持。`); @@ -151,16 +271,22 @@ function showErrorModal(message: string) { errorModal.style.display = 'block'; } -// 初始化 +// Modify the initialize function to create the first tab async function initialize() { - // 检查登录状态 + // Check login status checkLoginStatus(); try { const menuList = await getMenuList(); renderMenu(menuList); - // 绑定退出登录事件 + // Create initial tab + const initialTab = createTab('首页', 'about:blank'); + const tabHeader = createTabHeader(initialTab); + document.getElementById('tabsList')?.appendChild(tabHeader); + activateTab(initialTab.id); + + // Bind logout event const logoutBtn = document.getElementById('btnLogout'); if (logoutBtn) { logoutBtn.addEventListener('click', handleLogout); @@ -168,12 +294,12 @@ async function initialize() { const errorModal = document.getElementById('errorModal') as HTMLDivElement; const closeErrorModal = document.getElementById('closeErrorModal') as HTMLButtonElement; - // 关闭按钮点击事件 + // Close button click event closeErrorModal.addEventListener('click', (event) => { errorModal.style.display = 'none'; }); - // 点击窗口外部关闭 + // Click outside to close window.addEventListener('click', (event) => { if (event.target === errorModal) { errorModal.style.display = 'none'; diff --git a/src/main.ts b/src/main.ts index dea7502..3d89c89 100644 --- a/src/main.ts +++ b/src/main.ts @@ -109,6 +109,11 @@ const createWindow = () => { }, }); + // 隐藏顶部菜单栏 + win.setMenuBarVisibility(false); + win.setAutoHideMenuBar(true); + win.setMenu(null); + // // 配置 webview 的权限 // win.webContents.session.webRequest.onBeforeSendHeaders((details, callback) => { // callback({ @@ -131,11 +136,6 @@ const createWindow = () => { // }); // }); - // 隐藏顶部菜单栏 - win.setMenuBarVisibility(false); - win.setAutoHideMenuBar(true); - win.setMenu(null); - // 设置session win.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => { callback(true);