From 20f902ed2cd64c79dc08c3366b5804a3f18808d2 Mon Sep 17 00:00:00 2001 From: Netfan Date: Wed, 9 Apr 2025 04:05:27 +0800 Subject: [PATCH] feat: electron control buttons --- packages/@core/base/icons/src/lucide.ts | 3 ++ packages/@core/base/typings/electron.d.ts | 7 ++- .../src/components/layout-header.vue | 16 ++++++- packages/effects/layouts/package.json | 3 ++ .../layouts/src/authentication/toolbar.vue | 10 +++++ .../layouts/src/basic/header/header.vue | 19 ++++++++ .../src/widgets/app-close/app-close.vue | 16 +++++++ .../layouts/src/widgets/app-close/index.ts | 1 + .../src/widgets/app-maximize/app-maximize.vue | 30 +++++++++++++ .../layouts/src/widgets/app-maximize/index.ts | 1 + .../src/widgets/app-minimize/app-minimize.vue | 16 +++++++ .../layouts/src/widgets/app-minimize/index.ts | 1 + packages/effects/layouts/src/widgets/index.ts | 3 ++ packages/effects/layouts/tsconfig.json | 3 ++ playground/electron/main.ts | 45 ++++++++++++++++++- playground/electron/preload.ts | 1 - pnpm-lock.yaml | 4 ++ 17 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 packages/effects/layouts/src/widgets/app-close/app-close.vue create mode 100644 packages/effects/layouts/src/widgets/app-close/index.ts create mode 100644 packages/effects/layouts/src/widgets/app-maximize/app-maximize.vue create mode 100644 packages/effects/layouts/src/widgets/app-maximize/index.ts create mode 100644 packages/effects/layouts/src/widgets/app-minimize/app-minimize.vue create mode 100644 packages/effects/layouts/src/widgets/app-minimize/index.ts diff --git a/packages/@core/base/icons/src/lucide.ts b/packages/@core/base/icons/src/lucide.ts index 70e6a426f..5dcc32c79 100644 --- a/packages/@core/base/icons/src/lucide.ts +++ b/packages/@core/base/icons/src/lucide.ts @@ -45,6 +45,7 @@ export { Menu, Minimize, Minimize2, + Minus, MoonStar, Palette, PanelLeft, @@ -58,6 +59,8 @@ export { Settings, Shrink, Square, + SquareArrowDownLeft, + SquareArrowUpRight, SquareCheckBig, SquareMinus, Sun, diff --git a/packages/@core/base/typings/electron.d.ts b/packages/@core/base/typings/electron.d.ts index 129d77d84..d77d92a98 100644 --- a/packages/@core/base/typings/electron.d.ts +++ b/packages/@core/base/typings/electron.d.ts @@ -1,6 +1,11 @@ import type { IpcRendererEvent } from 'electron'; -export type IpcRendererInvoke = 'open-win'; +export type IpcRendererInvoke = + | 'app-close' + | 'app-maximize' + | 'app-minimize' + | 'is-maximized' + | 'open-win'; declare global { interface Window { diff --git a/packages/@core/ui-kit/layout-ui/src/components/layout-header.vue b/packages/@core/ui-kit/layout-ui/src/components/layout-header.vue index 534e4162c..a58e9de85 100644 --- a/packages/@core/ui-kit/layout-ui/src/components/layout-header.vue +++ b/packages/@core/ui-kit/layout-ui/src/components/layout-header.vue @@ -64,7 +64,7 @@ const logoStyle = computed((): CSSProperties => {
@@ -75,3 +75,17 @@ const logoStyle = computed((): CSSProperties => {
+ diff --git a/packages/effects/layouts/package.json b/packages/effects/layouts/package.json index 617f32316..dd464fab1 100644 --- a/packages/effects/layouts/package.json +++ b/packages/effects/layouts/package.json @@ -39,5 +39,8 @@ "@vueuse/core": "catalog:", "vue": "catalog:", "vue-router": "catalog:" + }, + "devDependencies": { + "@vben-core/typings": "workspace:*" } } diff --git a/packages/effects/layouts/src/authentication/toolbar.vue b/packages/effects/layouts/src/authentication/toolbar.vue index 94d321ab0..87f71e580 100644 --- a/packages/effects/layouts/src/authentication/toolbar.vue +++ b/packages/effects/layouts/src/authentication/toolbar.vue @@ -6,6 +6,9 @@ import { computed } from 'vue'; import { preferences } from '@vben/preferences'; import { + AppClose, + AppMaxmize, + AppMinmize, AuthenticationColorToggle, AuthenticationLayoutToggle, LanguageToggle, @@ -28,6 +31,8 @@ const showColor = computed(() => props.toolbarList.includes('color')); const showLayout = computed(() => props.toolbarList.includes('layout')); const showLanguage = computed(() => props.toolbarList.includes('language')); const showTheme = computed(() => props.toolbarList.includes('theme')); + +const isElectron = window?.ipcRenderer !== undefined; diff --git a/packages/effects/layouts/src/basic/header/header.vue b/packages/effects/layouts/src/basic/header/header.vue index 7a8cc857a..c9e8405ee 100644 --- a/packages/effects/layouts/src/basic/header/header.vue +++ b/packages/effects/layouts/src/basic/header/header.vue @@ -9,6 +9,9 @@ import { useAccessStore } from '@vben/stores'; import { VbenFullScreen, VbenIconButton } from '@vben-core/shadcn-ui'; import { + AppClose, + AppMaxmize, + AppMinmize, GlobalSearch, LanguageToggle, PreferencesButton, @@ -41,6 +44,13 @@ const { refresh } = useRefresh(); const rightSlots = computed(() => { const list = [{ index: REFERENCE_VALUE + 100, name: 'user-dropdown' }]; + if (window.ipcRenderer) { + list.push( + { index: REFERENCE_VALUE + 110, name: 'app-minimize' }, + { index: REFERENCE_VALUE + 120, name: 'app-maximize' }, + { index: REFERENCE_VALUE + 120, name: 'app-close' }, + ); + } if (preferences.widget.globalSearch) { list.push({ index: REFERENCE_VALUE, @@ -166,6 +176,15 @@ function clearPreferencesAndLogout() { + + + diff --git a/packages/effects/layouts/src/widgets/app-close/app-close.vue b/packages/effects/layouts/src/widgets/app-close/app-close.vue new file mode 100644 index 000000000..828ae3b4e --- /dev/null +++ b/packages/effects/layouts/src/widgets/app-close/app-close.vue @@ -0,0 +1,16 @@ + + diff --git a/packages/effects/layouts/src/widgets/app-close/index.ts b/packages/effects/layouts/src/widgets/app-close/index.ts new file mode 100644 index 000000000..2e2d84a01 --- /dev/null +++ b/packages/effects/layouts/src/widgets/app-close/index.ts @@ -0,0 +1 @@ +export { default as AppClose } from './app-close.vue'; diff --git a/packages/effects/layouts/src/widgets/app-maximize/app-maximize.vue b/packages/effects/layouts/src/widgets/app-maximize/app-maximize.vue new file mode 100644 index 000000000..92f9adf6f --- /dev/null +++ b/packages/effects/layouts/src/widgets/app-maximize/app-maximize.vue @@ -0,0 +1,30 @@ + + diff --git a/packages/effects/layouts/src/widgets/app-maximize/index.ts b/packages/effects/layouts/src/widgets/app-maximize/index.ts new file mode 100644 index 000000000..8f37f3eca --- /dev/null +++ b/packages/effects/layouts/src/widgets/app-maximize/index.ts @@ -0,0 +1 @@ +export { default as AppMaxmize } from './app-maximize.vue'; diff --git a/packages/effects/layouts/src/widgets/app-minimize/app-minimize.vue b/packages/effects/layouts/src/widgets/app-minimize/app-minimize.vue new file mode 100644 index 000000000..01f4e5426 --- /dev/null +++ b/packages/effects/layouts/src/widgets/app-minimize/app-minimize.vue @@ -0,0 +1,16 @@ + + diff --git a/packages/effects/layouts/src/widgets/app-minimize/index.ts b/packages/effects/layouts/src/widgets/app-minimize/index.ts new file mode 100644 index 000000000..b2e8a2770 --- /dev/null +++ b/packages/effects/layouts/src/widgets/app-minimize/index.ts @@ -0,0 +1 @@ +export { default as AppMinmize } from './app-minimize.vue'; diff --git a/packages/effects/layouts/src/widgets/index.ts b/packages/effects/layouts/src/widgets/index.ts index f6a4a7ba5..3d6d6954e 100644 --- a/packages/effects/layouts/src/widgets/index.ts +++ b/packages/effects/layouts/src/widgets/index.ts @@ -1,3 +1,6 @@ +export * from './app-close'; +export * from './app-maximize'; +export * from './app-minimize'; export { default as Breadcrumb } from './breadcrumb.vue'; export * from './check-updates'; export { default as AuthenticationColorToggle } from './color-toggle.vue'; diff --git a/packages/effects/layouts/tsconfig.json b/packages/effects/layouts/tsconfig.json index ce1a891fb..4ef032b28 100644 --- a/packages/effects/layouts/tsconfig.json +++ b/packages/effects/layouts/tsconfig.json @@ -1,6 +1,9 @@ { "$schema": "https://json.schemastore.org/tsconfig", "extends": "@vben/tsconfig/web.json", + "compilerOptions": { + "types": ["@vben-core/typings/electron"] + }, "include": ["src"], "exclude": ["node_modules"] } diff --git a/playground/electron/main.ts b/playground/electron/main.ts index 3b79adc0d..6bf2816b1 100644 --- a/playground/electron/main.ts +++ b/playground/electron/main.ts @@ -41,8 +41,10 @@ const indexHtml = path.join(RENDERER_DIST, 'index.html'); async function createWindow() { win = new BrowserWindow({ autoHideMenuBar: true, + frame: false, height: 900, icon: path.join(process.env.VITE_PUBLIC as string, 'favicon.ico'), + movable: true, show: false, title: 'Main window', webPreferences: { @@ -60,6 +62,14 @@ async function createWindow() { win?.show(); // 显示窗口 }); + win.on('maximize', () => { + win?.webContents.send('maximize-changed', true); + }); + + win.on('unmaximize', () => { + win?.webContents.send('maximize-changed', false); + }); + if (VITE_DEV_SERVER_URL) { win.loadURL(VITE_DEV_SERVER_URL); } else { @@ -132,6 +142,7 @@ app.on('activate', () => { // New window example arg: new windows url ipcMain.handle('open-win', (_, arg) => { const childWindow = new BrowserWindow({ + frame: false, webPreferences: { contextIsolation: true, nodeIntegration: true, @@ -142,8 +153,40 @@ ipcMain.handle('open-win', (_, arg) => { if (VITE_DEV_SERVER_URL) { childWindow.loadURL(`${VITE_DEV_SERVER_URL}#${arg}`); - childWindow.webContents.openDevTools(); } else { childWindow.loadFile(indexHtml, { hash: arg }); } }); + +ipcMain.handle('app-minimize', (event) => { + const browserWindow = BrowserWindow.fromWebContents(event.sender); + if (browserWindow) { + browserWindow.minimize(); + } +}); + +ipcMain.handle('app-maximize', (event) => { + const browserWindow = BrowserWindow.fromWebContents(event.sender); + if (browserWindow) { + if (browserWindow.isMaximized()) { + browserWindow.restore(); + } else { + browserWindow.maximize(); + } + } +}); + +ipcMain.handle('app-close', (event) => { + const browserWindow = BrowserWindow.fromWebContents(event.sender); + if (browserWindow) { + browserWindow.close(); + } +}); + +ipcMain.handle('is-maximized', (event) => { + const browserWindow = BrowserWindow.fromWebContents(event.sender); + if (browserWindow) { + return browserWindow.isMaximized(); + } + return false; +}); diff --git a/playground/electron/preload.ts b/playground/electron/preload.ts index 270379042..aebafef15 100644 --- a/playground/electron/preload.ts +++ b/playground/electron/preload.ts @@ -20,7 +20,6 @@ contextBridge.exposeInMainWorld('ipcRenderer', { const [channel, ...omit] = args; return ipcRenderer.send(channel, ...omit); }, - // You can expose other APTs you need here. // ... }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad2f1f3ff..c286f8343 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1687,6 +1687,10 @@ importers: vue-router: specifier: 'catalog:' version: 4.5.0(vue@3.5.13(typescript@5.8.3)) + devDependencies: + '@vben-core/typings': + specifier: workspace:* + version: link:../../@core/base/typings packages/effects/plugins: dependencies: