diff --git a/src/controller/tab-manager/index.ts b/src/controller/tab-manager/index.ts index 4c90ec6..8bcbad5 100644 --- a/src/controller/tab-manager/index.ts +++ b/src/controller/tab-manager/index.ts @@ -11,12 +11,25 @@ export class TabManager { private views: Map = new Map() private activeId: TabId | null = null private skipNextNavigate: Map = new Map() + private enabled = false constructor(win: BrowserWindow) { this.win = win this.win.on('resize', () => this.updateActiveBounds()) } + enable() { + this.enabled = true + this.updateActiveBounds() + if (this.activeId) this.attach(this.activeId) + } + + disable() { + this.enabled = false + const view = this.activeId ? this.views.get(this.activeId) : null + if (view) this.win.removeBrowserView(view) + } + list(): TabInfo[] { return Array.from(this.views.entries()).map(([id, view]) => this.info(id, view)) } @@ -25,7 +38,7 @@ export class TabManager { const id = randomUUID() const view = new BrowserView({ webPreferences: { sandbox: true } }) this.views.set(id, view) - this.attach(id) + if (this.enabled) this.attach(id) const target = url && url.length > 0 ? url : 'about:blank' view.webContents.loadURL(target) this.bindEvents(id, view) @@ -36,7 +49,7 @@ export class TabManager { switch(tabId: TabId): void { if (!this.views.has(tabId)) return - this.attach(tabId) + if (this.enabled) this.attach(tabId) this.win.webContents.send('tab-switched', { tabId }) } @@ -81,6 +94,7 @@ export class TabManager { } private attach(tabId: TabId): void { + if (!this.enabled) return const view = this.views.get(tabId) if (!view) return if (this.activeId && this.views.get(this.activeId)) { @@ -93,7 +107,7 @@ export class TabManager { } private updateActiveBounds(): void { - if (!this.activeId) return + if (!this.enabled || !this.activeId) return const view = this.views.get(this.activeId) if (!view) return const [width, height] = this.win.getContentSize() diff --git a/src/main.ts b/src/main.ts index b10361f..888ec03 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,92 +2,123 @@ import { app, BrowserWindow, ipcMain, shell } from "electron"; import path from "node:path"; import started from "electron-squirrel-startup"; import { TabManager } from './controller/tab-manager' -import { registerTabIpc } from './ipc' import "./controller/window-size-controll"; -// Handle creating/removing shortcuts on Windows when installing/uninstalling. if (started) { app.quit(); } -const isDev = !!MAIN_WINDOW_VITE_DEV_SERVER_URL; -const createWindow = () => { - // Create the browser window. - const mainWindow = new BrowserWindow({ - width: 900, - height: 670, - autoHideMenuBar: true, - frame: false, - windowButtonVisibility: false, - resizable: true, - maximizable: true, - minimizable: true, - webPreferences: { - devTools: isDev, - nodeIntegration: false, - contextIsolation: true, - sandbox: true, - preload: path.join(__dirname, "preload.js"), - }, - }); +class AppMain { + private mainWindow: BrowserWindow | null = null + private tabs: TabManager | null = null + private readonly isDev = !!MAIN_WINDOW_VITE_DEV_SERVER_URL - ipcMain.handle('open-baidu', () => { - mainWindow.loadURL("https://www.baidu.com") - }) + init() { + this.registerLifecycle() + this.registerCommonIPC() + this.registerAppIPC() + } - ipcMain.handle("external-open", (_event, url: string) => { - try { - const allowed = /^https:\/\/(www\.)?(baidu\.com|\w+[\.-]?\w+\.[a-z]{2,})(\/.*)?$/i; - if (typeof url === "string" && allowed.test(url)) { - return shell.openExternal(url); - } - throw new Error("URL not allowed"); - } catch (e) { - return Promise.reject(e); + private createWindow(options?: { frameless?: boolean; route?: string }): BrowserWindow { + const frameless = !!options?.frameless + const win = new BrowserWindow({ + width: 900, + height: 670, + autoHideMenuBar: true, + frame: frameless ? false : true, + // @ts-ignore + windowButtonVisibility: frameless ? false : true, + resizable: true, + maximizable: true, + minimizable: true, + webPreferences: { + devTools: this.isDev, + nodeIntegration: false, + contextIsolation: true, + sandbox: true, + preload: path.join(__dirname, "preload.js"), + }, + }) + + this.loadEntry(win, options?.route) + if (this.isDev) win.webContents.openDevTools() + this.mainWindow = win + this.initTabsIfNeeded(options) + return win + } + + private loadEntry(win: BrowserWindow, route?: string) { + // @ts-ignore + if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { + const target = route ? `${MAIN_WINDOW_VITE_DEV_SERVER_URL}${route}` : MAIN_WINDOW_VITE_DEV_SERVER_URL + win.loadURL(target) + } else { + win.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`)) } - }); - - // and load the index.html of the app. - // @ts-ignore - if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { - mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL); - } else { - mainWindow.loadFile( - path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`) - ); } - if (isDev) { - mainWindow.webContents.openDevTools(); + private initTabsIfNeeded(options?: { frameless?: boolean; route?: string }) { + if (!this.mainWindow) return + const route = options?.route || '' + const shouldInit = !!options?.frameless || route.startsWith('/browser') + if (!shouldInit) return + this.tabs = new TabManager(this.mainWindow) + this.registerTabIPC() + this.tabs.enable?.() + this.tabs.create('about:blank') } - // const tabs = new TabManager(mainWindow) - // registerTabIpc(tabs) - // tabs.create('about:blank') -}; - -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.on("ready", createWindow); - -// Quit when all windows are closed, except on macOS. There, it's common -// for applications and their menu bar to stay active until the user quits -// explicitly with Cmd + Q. -app.on("window-all-closed", () => { - if (process.platform !== "darwin") { - app.quit(); + private registerLifecycle() { + app.on("ready", () => { + this.createWindow({ frameless: false, route: '/login' }) + }) + app.on("window-all-closed", () => { + if (process.platform !== "darwin") app.quit() + }) + app.on("activate", () => { + if (!BrowserWindow.getAllWindows().length) this.createWindow({ frameless: false, route: '/login' }) + }) } -}); -app.on("activate", () => { - // On OS X it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) { - createWindow(); + private registerCommonIPC() { + ipcMain.handle('open-baidu', () => { + this.mainWindow?.loadURL("https://www.baidu.com") + }) + ipcMain.handle("external-open", (_event, url: string) => { + try { + const allowed = /^https:\/\/(www\.)?(baidu\.com|\w+[\.-]?\w+\.[a-z]{2,})(\/.*)?$/i; + if (typeof url === "string" && allowed.test(url)) { + return shell.openExternal(url); + } + throw new Error("URL not allowed"); + } catch (e) { + return Promise.reject(e); + } + }) } -}); -// In this file you can include the rest of your app's specific main process -// code. You can also put them in separate files and import them here. + private registerAppIPC() { + ipcMain.handle('app:set-frameless', async (event, route?: string) => { + const old = BrowserWindow.fromWebContents(event.sender) + const win = this.createWindow({ frameless: true, route }) + if (old && !old.isDestroyed()) old.close() + return true + }) + } + + private registerTabIPC() { + if (!this.tabs) return + const tabs = this.tabs + ipcMain.handle('tab:create', (_e, url?: string) => tabs.create(url)) + ipcMain.handle('tab:list', () => tabs.list()) + ipcMain.handle('tab:navigate', (_e, payload: { tabId: string; url: string }) => tabs.navigate(payload.tabId, payload.url)) + ipcMain.handle('tab:reload', (_e, tabId: string) => tabs.reload(tabId)) + ipcMain.handle('tab:back', (_e, tabId: string) => tabs.goBack(tabId)) + ipcMain.handle('tab:forward', (_e, tabId: string) => tabs.goForward(tabId)) + ipcMain.handle('tab:switch', (_e, tabId: string) => tabs.switch(tabId)) + ipcMain.handle('tab:close', (_e, tabId: string) => tabs.close(tabId)) + } +} + +new AppMain().init() diff --git a/src/preload.ts b/src/preload.ts index 29cdd92..6ed39cf 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -14,6 +14,9 @@ contextBridge.exposeInMainWorld('api', { maximize: () => ipcRenderer.send('window-max'), close: () => ipcRenderer.send('window-close') }, + app: { + setFrameless: (route?: string) => ipcRenderer.invoke('app:set-frameless', route) + }, tabs: { create: (url?: string) => ipcRenderer.invoke('tab:create', url), list: () => ipcRenderer.invoke('tab:list'), diff --git a/src/router/index.ts b/src/router/index.ts index 222006e..cda5f5a 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -2,7 +2,7 @@ import { createRouter, createWebHistory } from "vue-router"; const routes = [ { - path: "/", + path: "/login", name: "Login", component: () => import("@/views/login/index.vue"), }, @@ -27,10 +27,10 @@ const router = createRouter({ router.beforeEach((to, _from, next) => { const token = localStorage.getItem("token"); if (to.meta && (to.meta as any).requiresAuth && !token) { - next({ path: "/" }); + next({ path: "/login" }); return; } - if (to.path === "/" && token) { + if (token && to.path !== "/browser") { next({ path: "/browser" }); return; } diff --git a/src/views/login/index.vue b/src/views/login/index.vue index db8ad2e..e27ba01 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -42,7 +42,6 @@ @@ -72,24 +71,18 @@ const validate = () => { return !errors.account && !errors.password; }; -const valid = ref(false); - -const recalc = () => { - valid.value = validate(); -}; - const onSubmit = async () => { - recalc(); - if (!valid.value || loading.value) return; - loading.value = true; + // if (!validate() || loading.value) return; + // loading.value = true; try { - const res: any = await apiLogin({ account: form.account, password: form.password }); - const token = res && (res.token || res.data?.token || res.access_token); - if (!token) throw new Error("登录失败"); - localStorage.setItem("token", token); - router.replace("/browser"); + localStorage.setItem("token", "dev-token"); + // const res: any = await apiLogin({ account: form.account, password: form.password }); + // const token = res && (res.token || res.data?.token || res.access_token); + // if (!token) throw new Error("登录失败"); + // localStorage.setItem("token", token); + await (window as any).api.app.setFrameless('/browser') } finally { - loading.value = false; + // loading.value = false; } };