diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 0000000..c03a1d0 --- /dev/null +++ b/global.d.ts @@ -0,0 +1,7 @@ +declare module "@store/*"; +declare module "@utils/*"; +declare module "@assets/images/*"; +declare module "@constant/*"; +declare module "@remixicon/vue"; +declare module "vue-router"; + diff --git a/shims-vue.d.ts b/shims-vue.d.ts deleted file mode 100644 index a622f87..0000000 --- a/shims-vue.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module '*.vue' { - import type { DefineComponent } from 'vue' - // 泛型参数:Props类型、Emits类型、Slots类型(默认任意) - const component: DefineComponent<{}, {}, any> - export default component -} diff --git a/src/common/constants.ts b/src/common/constants.ts new file mode 100644 index 0000000..653125e --- /dev/null +++ b/src/common/constants.ts @@ -0,0 +1,108 @@ +// 渲染进程|主进程常量定义 + +export enum IPC_EVENTS { + EXTERNAL_OPEN = 'external-open', + WINDOW_MINIMIZE = 'window-minimize', + WINDOW_MAXIMIZE = 'window-maximize', + WINDOW_CLOSE = 'window-close', + APP_SET_FRAMELESS = 'app:set-frameless', + TAB_CREATE = 'tab:create', + TAB_LIST = 'tab:list', + TAB_NAVIGATE = 'tab:navigate', + TAB_RELOAD = 'tab:reload', + TAB_BACK = 'tab:back', + TAB_FORWARD = 'tab:forward', + TAB_SWITCH = 'tab:switch', + TAB_CLOSE = 'tab:close', + LOG_TO_MAIN = 'log-to-main', + READ_FILE = 'read-file', + INVOKE = 'ipc:invoke', + INVOKE_ASYNC = 'ipc:invokeAsync', + APP_MINIMIZE ='app:minimize', + APP_MAXIMIZE ='app:maximize', + APP_QUIT ='app:quit', + FILE_READ = 'file:read', + FILE_WRITE = 'file:write', + GET_WINDOW_ID='get-window-id', + CUSTOM_EVENT ='custom:event', + TIME_UPDATE = 'time:update' +} + +export const MAIN_WIN_SIZE = { + width: 1440, + height: 900, + minWidth: 1440, + minHeight: 900, +} as const + + +// 定义每个通道的参数和返回值类型 +interface IPCTypings { + // 同步通信 + [IPC_EVENTS.APP_MINIMIZE]: { + params: [window:number] + return: {success: boolean, error?: string} + } + [IPC_EVENTS.APP_MAXIMIZE]: { + params: [window:number] + return: {success: boolean, error?: string} + } + [IPC_EVENTS.GET_WINDOW_ID]: { + params: [] + return: number + } + + // 异步通信 + [IPC_EVENTS.FILE_READ]: { + params: [filePath: string] + return: Promise<{success: boolean, data?: string, error?: string}> + } + [IPC_EVENTS.FILE_WRITE]: { + params: [filePath: string, content: string] + return: Promise<{success: boolean, error?: string}> + } + + // 事件通信 + [IPC_EVENTS.TIME_UPDATE]: { + params: [time: string] + return: void + } + [IPC_EVENTS.CUSTOM_EVENT]: { + params: [message: string] + return: void + } +} + +// 定义IPC API 接口 +export interface WindowApi { + invoke(channel: T, ...args: IPCTypings[T]['params']): IPCTypings[T]['return'], + invokeAsync(channel: T, ...args: IPCTypings[T]['params']): IPCTypings[T]['return'], + on(channel: T, callback: (...args: IPCTypings[T]['params']) => void): () => void + send(channel: T, ...args: IPCTypings[T]['params']): void, + getCurrentWindowId(): number, + versions: NodeJS.ProcessVersions, + external: { + open: (url: string) => void + }, + window: { + minimize: () => void, + maximize: () => void, + close: () => void + }, + app: { + setFrameless: (route?: string) => void + }, + tabs: { + create: (url?: string) => void, + list: () => void, + navigate: (tabId: string, url: string) => void, + reload: (tabId: string) => void, + back: (tabId: string) => void, + forward: (tabId: string) => void, + switch: (tabId: string) => void, + close: (tabId: string) => void, + on: (event: 'tab-updated' | 'tab-created' | 'tab-closed' | 'tab-switched', handler: (payload: any) => void) => void + }, + readFile: (filePath: string) => Promise<{success: boolean, data?: string, error?: string}>, + logToMain: (logLevel: string, message: string) => void, +} diff --git a/src/preload.ts b/src/preload.ts index 1c58a1c..bd0e09f 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -1,7 +1,7 @@ import { contextBridge, ipcRenderer } from 'electron' -import { IPCChannel, IPCAPI } from '@/shared/types/ipc.types' +import { IPC_EVENTS, WindowApi } from '@common/constants'; -const api: IPCAPI = { +const api: WindowApi = { versions: process.versions, external: { @@ -9,24 +9,24 @@ const api: IPCAPI = { }, window: { - minimize: () => ipcRenderer.send('window-min'), - maximize: () => ipcRenderer.send('window-max'), - close: () => ipcRenderer.send('window-close') + minimize: () => ipcRenderer.send(IPC_EVENTS.WINDOW_MINIMIZE), + maximize: () => ipcRenderer.send(IPC_EVENTS.WINDOW_MAXIMIZE), + close: () => ipcRenderer.send(IPC_EVENTS.WINDOW_CLOSE) }, app: { - setFrameless: (route?: string) => ipcRenderer.invoke('app:set-frameless', route) + setFrameless: (route?: string) => ipcRenderer.invoke(IPC_EVENTS.APP_SET_FRAMELESS, route) }, tabs: { - create: (url?: string) => ipcRenderer.invoke('tab:create', url), - list: () => ipcRenderer.invoke('tab:list'), - navigate: (tabId: string, url: string) => ipcRenderer.invoke('tab:navigate', { tabId, url }), - reload: (tabId: string) => ipcRenderer.invoke('tab:reload', tabId), - back: (tabId: string) => ipcRenderer.invoke('tab:back', tabId), - forward: (tabId: string) => ipcRenderer.invoke('tab:forward', tabId), - switch: (tabId: string) => ipcRenderer.invoke('tab:switch', tabId), - close: (tabId: string) => ipcRenderer.invoke('tab:close', tabId), + create: (url?: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_CREATE, url), + list: () => ipcRenderer.invoke(IPC_EVENTS.TAB_LIST), + navigate: (tabId: string, url: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_NAVIGATE, { tabId, url }), + reload: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_RELOAD, tabId), + back: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_BACK, tabId), + forward: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_FORWARD, tabId), + switch: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_SWITCH, tabId), + close: (tabId: string) => ipcRenderer.invoke(IPC_EVENTS.TAB_CLOSE, tabId), on: (event: 'tab-updated' | 'tab-created' | 'tab-closed' | 'tab-switched', handler: (payload: any) => void) => { const listener = (_e: any, payload: any) => handler(payload) ipcRenderer.on(event, listener) @@ -35,38 +35,35 @@ const api: IPCAPI = { }, // 通过 IPC 调用主进程 - readFile: (filePath: string) => ipcRenderer.invoke('read-file', filePath), + readFile: (filePath: string) => ipcRenderer.invoke(IPC_EVENTS.READ_FILE, filePath), // 同步调用 - invoke: (channel: IPCChannel, ...args: any[]) => ipcRenderer.sendSync('ipc:invoke', channel, ...args), + invoke: (channel: IPC_EVENTS, ...args: any[]) => ipcRenderer.sendSync(IPC_EVENTS.INVOKE, channel, ...args), // 异步调用 - invokeAsync: (channel: IPCChannel, ...args: any[]) => { + invokeAsync: (channel: IPC_EVENTS, ...args: any[]) => { try { - ipcRenderer.invoke('ipc:invokeAsync', channel, ...args) + ipcRenderer.invoke(IPC_EVENTS.INVOKE_ASYNC, channel, ...args) } catch (error) { throw error } }, // 监听主进程消息 - on: (event: IPCChannel, callback: (...args: any[]) => void) => { + on: (event: IPC_EVENTS, callback: (...args: any[]) => void) => { const subscription = (_event: any, ...args: any[]) => callback(...args) ipcRenderer.on(event, subscription) return () => ipcRenderer.removeListener(event, subscription) }, // 发送消息到主进程 - send: (channel: IPCChannel, ...args: any[]) => ipcRenderer.send(channel, ...args), + send: (channel: IPC_EVENTS, ...args: any[]) => ipcRenderer.send(channel, ...args), // 获取窗口ID - getCurrentWindowId: () => ipcRenderer.sendSync(IPCChannel.GET_WINDOW_ID), + getCurrentWindowId: () => ipcRenderer.sendSync(IPC_EVENTS.GET_WINDOW_ID), // 发送日志 - logToMain: (logLevel: string, message: string) => ipcRenderer.send('log-to-main', logLevel, message), - - // 打开新窗口 - openNewTab: (url: string) => ipcRenderer.invoke('open-new-tab', url) + logToMain: (logLevel: string, message: string) => ipcRenderer.send(IPC_EVENTS.LOG_TO_MAIN, logLevel, message), } -contextBridge.exposeInMainWorld('ipcAPI', api) \ No newline at end of file +contextBridge.exposeInMainWorld('api', api) \ No newline at end of file diff --git a/src/renderer/browser/BrowserLayout.vue b/src/renderer/browser/BrowserLayout.vue index 106a7f2..3fc5328 100644 --- a/src/renderer/browser/BrowserLayout.vue +++ b/src/renderer/browser/BrowserLayout.vue @@ -57,26 +57,26 @@ const refreshActiveAddress = () => { } const syncList = async () => { - const list: TabInfo[] = await (window as any).ipcAPI.tabs.list() + const list: TabInfo[] = await (window as any).api.tabs.list() tabs.splice(0, tabs.length, ...list) if (!activeId.value && list.length > 0) activeId.value = list[0].id refreshActiveAddress() } const onNewTab = async () => { - const info: TabInfo = await (window as any).ipcAPI.tabs.create('about:blank') + const info: TabInfo = await (window as any).api.tabs.create('about:blank') activeId.value = info.id } const onSwitch = async (id: string) => { - await (window as any).ipcAPI.tabs.switch(id) + await (window as any).api.tabs.switch(id) activeId.value = id refreshActiveAddress() } const onCloseTab = async () => { if (!activeId.value) return - await (window as any).ipcAPI.tabs.close(activeId.value) + await (window as any).api.tabs.close(activeId.value) } const normalizeUrl = (u: string) => { @@ -90,46 +90,46 @@ const onNavigate = async () => { if (!activeId.value || !address.value) return const url = normalizeUrl(address.value) if (!url) return - await (window as any).ipcAPI.tabs.navigate(activeId.value, url) + await (window as any).api.tabs.navigate(activeId.value, url) } const onReload = async () => { if (!activeId.value) return - await (window as any).ipcAPI.tabs.reload(activeId.value) + await (window as any).api.tabs.reload(activeId.value) } const onBack = async () => { if (!activeId.value) return - await (window as any).ipcAPI.tabs.back(activeId.value) + await (window as any).api.tabs.back(activeId.value) } const onForward = async () => { if (!activeId.value) return - await (window as any).ipcAPI.tabs.forward(activeId.value) + await (window as any).api.tabs.forward(activeId.value) } const onCloseTabId = async (id: string) => { - await (window as any).ipcAPI.tabs.close(id) + await (window as any).api.tabs.close(id) } -const onCloseWindow = () => (window as any).ipcAPI.window.close() -const onMinimizeWindow = () => (window as any).ipcAPI.window.minimize() -const onMaximizeWindow = () => (window as any).ipcAPI.window.maximize() +const onCloseWindow = () => (window as any).api.window.close() +const onMinimizeWindow = () => (window as any).api.window.minimize() +const onMaximizeWindow = () => (window as any).api.window.maximize() onMounted(async () => { await syncList() - ; (window as any).ipcAPI.tabs.on('tab-created', (info: TabInfo) => { + ; (window as any).api.tabs.on('tab-created', (info: TabInfo) => { const i = tabs.findIndex(t => t.id === info.id) if (i === -1) tabs.push(info) activeId.value = info.id refreshActiveAddress() }) - ; (window as any).ipcAPI.tabs.on('tab-updated', (info: TabInfo) => { + ; (window as any).api.tabs.on('tab-updated', (info: TabInfo) => { const i = tabs.findIndex(t => t.id === info.id) if (i >= 0) tabs[i] = info if (activeId.value === info.id) refreshActiveAddress() }) - ; (window as any).ipcAPI.tabs.on('tab-closed', ({ tabId }: { tabId: string }) => { + ; (window as any).api.tabs.on('tab-closed', ({ tabId }: { tabId: string }) => { const i = tabs.findIndex(t => t.id === tabId) if (i >= 0) tabs.splice(i, 1) if (activeId.value === tabId) { @@ -138,7 +138,7 @@ onMounted(async () => { refreshActiveAddress() } }) - ; (window as any).ipcAPI.tabs.on('tab-switched', ({ tabId }: { tabId: string }) => { + ; (window as any).api.tabs.on('tab-switched', ({ tabId }: { tabId: string }) => { activeId.value = tabId refreshActiveAddress() }) diff --git a/src/renderer/browser/README.md b/src/renderer/browser/README.md index c06b324..58ef366 100644 --- a/src/renderer/browser/README.md +++ b/src/renderer/browser/README.md @@ -103,7 +103,7 @@ tab-switched: { tabId: TabId } 1. 核心能力(主进程 + IPC + 预加载) - 实现 `TabManager` 与全部导航方法 - 注册 IPC(tabs/bookmarks/plugins)与广播 - - 预加载扩展 `window.ipcAPI.tabs/*`、事件订阅封装 + - 预加载扩展 `window.api.tabs/*`、事件订阅封装 2. 渲染层界面 - 新建 `BrowserLayout` 与基础组件(TabBar、AddressBar、Controls) - 打通地址栏与导航;同步标题与加载状态 diff --git a/src/renderer/env.d.ts b/src/renderer/env.d.ts deleted file mode 100644 index 5eea330..0000000 --- a/src/renderer/env.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare module "@store/counter"; -declare module "@utils/request"; -declare module "@assets/images/*"; -declare module "@constant/rate"; -declare module "@constant/menus"; -declare module "@remixicon/vue"; -declare module "vue-router"; \ No newline at end of file diff --git a/src/renderer/views/login/index.vue b/src/renderer/views/login/index.vue index a77cf23..7ba0a82 100644 --- a/src/renderer/views/login/index.vue +++ b/src/renderer/views/login/index.vue @@ -105,7 +105,7 @@ const onSubmit = async () => { // const token = res && (res.token || res.data?.token || res.access_token); // if (!token) throw new Error("登录失败"); // localStorage.setItem("token", token); - // await (window as any).ipcAPI.app.setFrameless('/home') + // await (window as any).api.app.setFrameless('/home') router.push('/home'); } finally { // loading.value = false; diff --git a/src/shared/types/ipc.types.ts b/src/shared/types/ipc.types.ts deleted file mode 100644 index 1c54374..0000000 --- a/src/shared/types/ipc.types.ts +++ /dev/null @@ -1,83 +0,0 @@ -export enum IPCChannel { - APP_MINIMIZE ='app:minimize', - APP_MAXIMIZE ='app:maximize', - APP_QUIT ='app:quit', - FILE_READ = 'file:read', - FILE_WRITE = 'file:write', - GET_WINDOW_ID='get-window-id', - CUSTOM_EVENT ='custom:event', - TIME_UPDATE = 'time:update' -} - -// 定义每个通道的参数和返回值类型 -export interface IPCTypings { - // 同步通信 - [IPCChannel.APP_MINIMIZE]: { - params: [window:number] - return: {success: boolean, error?: string} - } - [IPCChannel.APP_MAXIMIZE]: { - params: [window:number] - return: {success: boolean, error?: string} - } - [IPCChannel.GET_WINDOW_ID]: { - params: [] - return: number - } - - // 异步通信 - [IPCChannel.FILE_READ]: { - params: [filePath: string] - return: Promise<{success: boolean, data?: string, error?: string}> - } - [IPCChannel.FILE_WRITE]: { - params: [filePath: string, content: string] - return: Promise<{success: boolean, error?: string}> - } - - // 事件通信 - [IPCChannel.TIME_UPDATE]: { - params: [time: string] - return: void - } - [IPCChannel.CUSTOM_EVENT]: { - params: [message: string] - return: void - } -} - -// 定义IPC API 接口 -export interface IPCAPI { - invoke(channel: T, ...args: IPCTypings[T]['params']): IPCTypings[T]['return'], - invokeAsync(channel: T, ...args: IPCTypings[T]['params']): IPCTypings[T]['return'], - on(channel: T, callback: (...args: IPCTypings[T]['params']) => void): () => void - send(channel: T, ...args: IPCTypings[T]['params']): void, - getCurrentWindowId(): number, - versions: NodeJS.ProcessVersions, - external: { - open: (url: string) => void - }, - window: { - minimize: () => void, - maximize: () => void, - close: () => void - }, - app: { - setFrameless: (route?: string) => void - }, - tabs: { - create: (url?: string) => void, - list: () => void, - navigate: (tabId: string, url: string) => void, - reload: (tabId: string) => void, - back: (tabId: string) => void, - forward: (tabId: string) => void, - switch: (tabId: string) => void, - close: (tabId: string) => void, - on: (event: 'tab-updated' | 'tab-created' | 'tab-closed' | 'tab-switched', handler: (payload: any) => void) => void - }, - readFile: (filePath: string) => Promise<{success: boolean, data?: string, error?: string}>, - logToMain: (logLevel: string, message: string) => void, - // 打开新窗口 - openNewTab: (url: string) => Promise, -} diff --git a/tsconfig.json b/tsconfig.json index 07d2801..8ff7fcd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "paths": { "@/*": ["src/renderer/*"], "@assets/*": ["src/assets/*"], + "@common/*": ["src/common/*"], "@store/*": ["src/renderer/store/*"], "@constant/*": ["src/renderer/constant/*"], "@utils/*": ["src/renderer/utils/*"], @@ -34,7 +35,9 @@ "src/**/*", "./package.json", "./forge.config.ts", - "*.ts", + "forge.env.d.ts", + "**/*.ts", + "**/*.d.ts", "vite.renderer.config.ts", "packages/electron-chrome-context-menu", "packages/chrome-ui" diff --git a/vite.main.config.ts b/vite.main.config.ts index 428bec0..f68017b 100644 --- a/vite.main.config.ts +++ b/vite.main.config.ts @@ -10,6 +10,7 @@ export default defineConfig( async () => { resolve: { alias: { "@": resolve(__dirname, "./src/main"), + '@common': resolve(__dirname, './src/common'), "@modules": resolve(__dirname, "./src/main/modules"), }, }, diff --git a/vite.preload.config.ts b/vite.preload.config.ts index e57f31a..f531aea 100644 --- a/vite.preload.config.ts +++ b/vite.preload.config.ts @@ -5,7 +5,8 @@ import { resolve } from "path"; export default defineConfig({ resolve: { alias: { - "@": resolve(__dirname, "./src") + "@": resolve(__dirname, "./src"), + '@common': resolve(__dirname, './src/common'), }, }, }); diff --git a/vite.renderer.config.ts b/vite.renderer.config.ts index a13a47f..9cd1fc8 100644 --- a/vite.renderer.config.ts +++ b/vite.renderer.config.ts @@ -21,11 +21,12 @@ export default defineConfig(async () => { preserveSymlinks: true, alias: { "@": resolve(__dirname, "./src/renderer"), - "@assets": resolve(__dirname, "./src/assets"), - "@store": resolve(__dirname, "./src/renderer/store"), - "@constant": resolve(__dirname, "./src/renderer/constant"), - "@utils": resolve(__dirname, "./src/renderer/utils"), "@api": resolve(__dirname, "./src/renderer/api"), + "@assets": resolve(__dirname, "./src/assets"), + '@common': resolve(__dirname, './src/common'), + "@constant": resolve(__dirname, "./src/renderer/constant"), + "@store": resolve(__dirname, "./src/renderer/store"), + "@utils": resolve(__dirname, "./src/renderer/utils"), "@/types": resolve(__dirname, "./src/renderer/types"), }, },