diff --git a/electron.vite.config.1749712333417.mjs b/electron.vite.config.1749712333417.mjs new file mode 100644 index 0000000..dd4359d --- /dev/null +++ b/electron.vite.config.1749712333417.mjs @@ -0,0 +1,133 @@ +// electron.vite.config.ts +import { resolve } from "path"; +import { defineConfig, externalizeDepsPlugin } from "electron-vite"; +import vue from "@vitejs/plugin-vue"; +import vueJsx from "@vitejs/plugin-vue-jsx"; +import UnoCSS from "unocss/vite"; +import AutoImport from "unplugin-auto-import/vite"; +import Components from "unplugin-vue-components/vite"; +import VueMacros from "unplugin-vue-macros/vite"; +import { VueRouterAutoImports } from "unplugin-vue-router"; +import VueRouter from "unplugin-vue-router/vite"; +import Layouts from "vite-plugin-vue-layouts"; +import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"; +import monacoEditorPlugin from "vite-plugin-monaco-editor"; +import IconsResolver from "unplugin-icons/resolver"; +import Icons from "unplugin-icons/vite"; +var __electron_vite_injected_dirname = "D:\\@code\\xyx\\electron-app"; +var electron_vite_config_default = defineConfig({ + main: { + resolve: { + alias: { + config: resolve("config"), + main: resolve("src/main"), + common: resolve("src/common"), + "@res": resolve("resources") + } + }, + plugins: [externalizeDepsPlugin()] + }, + preload: { + build: { + lib: { + entry: { + index: resolve(__electron_vite_injected_dirname, "./src/preload/index.ts"), + plugin: resolve(__electron_vite_injected_dirname, "./src/preload/plugin.ts") + } + } + }, + plugins: [externalizeDepsPlugin()] + }, + renderer: { + root: resolve(__electron_vite_injected_dirname, "./src/renderer"), + resolve: { + alias: { + config: resolve("config"), + common: resolve("src/common"), + "@": resolve("src/renderer/src"), + "@res": resolve("resources") + } + }, + css: { + preprocessorOptions: { + scss: { + additionalData: `@use "@/assets/style/global" as *; +`, + api: "modern-compiler" + } + } + }, + build: { + rollupOptions: { + input: { + main: resolve(__electron_vite_injected_dirname, "./src/renderer/index.html"), + about: resolve(__electron_vite_injected_dirname, "./src/renderer/about.html") + } + } + }, + plugins: [ + UnoCSS(), + VueMacros({ + plugins: { + vue: vue(), + vueJsx: vueJsx(), + vueRouter: VueRouter({ + root: resolve(__electron_vite_injected_dirname, "src/renderer"), + // https://github.com/posva/unplugin-vue-router + extensions: [".vue", ".setup.tsx"], + exclude: ["**/_ui"] + }) + } + }), + VueI18nPlugin({ + compositionOnly: false, + include: resolve(__electron_vite_injected_dirname, "packages/locales/languages/**") + }), + Layouts({ + layoutsDirs: "src/layouts", + pagesDirs: "src/pages", + defaultLayout: "default", + extensions: ["vue", "setup.tsx"], + exclude: ["**/_ui"] + }), + // https://github.com/antfu/unplugin-auto-import + AutoImport({ + imports: [ + "vue", + "@vueuse/core", + VueRouterAutoImports, + { + // add any other imports you were relying on + "vue-router/auto": ["useLink"] + }, + "vue-i18n" + ], + dts: true, + dirs: ["src/composables"], + vueTemplate: true + }), + // https://github.com/antfu/vite-plugin-components + Components({ + dts: true, + dirs: ["src/components", "src/ui"], + resolvers: [ + IconsResolver({ + prefix: "icon" + }) + ] + }), + Icons(), + // https://wf0.github.io/example/plugins/Formatter.html + // @ts-ignore ... + monacoEditorPlugin.default({ + publicPath: "monacoeditorwork", + customDistPath() { + return resolve(__electron_vite_injected_dirname, "out/renderer/monacoeditorwork"); + } + }) + ] + } +}); +export { + electron_vite_config_default as default +}; diff --git a/packages/utils/index.ts b/packages/utils/index.ts index e69de29..8c5ee26 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -0,0 +1,11 @@ +export function isPromise(value: () => any) { + return value && Object.prototype.toString.call(value) === "[object Promise]" +} + +export function slash(path: string) { + const isExtendedLengthPath = path.startsWith("\\\\?\\") + if (isExtendedLengthPath) { + return path + } + return path.replace(/\\/g, "/") +} diff --git a/packages/utils/main/index.ts b/packages/utils/main/index.ts index d78e8cd..4da7a0c 100644 --- a/packages/utils/main/index.ts +++ b/packages/utils/main/index.ts @@ -1,5 +1,22 @@ +import { is } from "@electron-toolkit/utils" import { webContents } from "electron" +import { join } from "node:path" +import { slash } from "utils" export const broadcast = void>>(event: keyof T, ...args: Parameters) => { webContents.getAllWebContents().forEach(browser => browser.send(event as any, ...args)) } + +export function getFileUrl(app: string) { + let winURL = "" + if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { + winURL = process.env["ELECTRON_RENDERER_URL"] + `/${app}#/` + } else { + winURL = join(__dirname, `../renderer/${app}#/`) + } + return slash(winURL) +} + +export function getPreloadUrl(file) { + return join(__dirname, `../preload/${file}.mjs`) +} diff --git a/src/main/utils/session/cookies.ts b/packages/utils/main/session/cookies.ts similarity index 100% rename from src/main/utils/session/cookies.ts rename to packages/utils/main/session/cookies.ts diff --git a/src/main/utils/session/index.ts b/packages/utils/main/session/index.ts similarity index 100% rename from src/main/utils/session/index.ts rename to packages/utils/main/session/index.ts diff --git a/src/common/event/PlatForm/main/command.ts b/src/common/event/PlatForm/main/command.ts index 28fcd72..97e09cd 100644 --- a/src/common/event/PlatForm/main/command.ts +++ b/src/common/event/PlatForm/main/command.ts @@ -3,10 +3,10 @@ import { inject } from "inversify" import errorHandler from "logger/main-error" import Tabs from "main/modules/tabs" import WindowManager from "main/modules/window-manager" -import { getFileUrl } from "main/utils" import icon from "@res/icon.png?asset" import setting from "setting/main" import { LogLevel } from "logger/common" +import { getFileUrl } from "utils/main" export default class PlatFormCommand { constructor( diff --git a/src/common/event/Setting/hook.ts b/src/common/event/Setting/hook.ts new file mode 100644 index 0000000..8456d3a --- /dev/null +++ b/src/common/event/Setting/hook.ts @@ -0,0 +1,63 @@ +import { defineStore } from "pinia" +import { Setting } from "." +import type { IConfig } from "config" + +let rawConfig: IConfig = Setting.getInstance().sync() as unknown as IConfig + +export const useSettingStore = defineStore( + "Setting", + () => { + const config = ref(JSON.parse(JSON.stringify(rawConfig))) + const diffKeys = ref<(keyof IConfig)[]>([]) + const isSame = computed(() => { + return diffKeys.value.length === 0 + }) + watch( + () => config.value, + () => { + diffKeys.value = [] + ;(Object.keys(config.value) as (keyof IConfig)[]).forEach((key: keyof IConfig) => { + if (config.value[key] !== rawConfig[key]) { + diffKeys.value.push(key) + } + }) + }, + { + deep: true, + immediate: true, + }, + ) + const reset = () => { + const tempConfig = JSON.parse(JSON.stringify(rawConfig)) + config.value = tempConfig + } + const isSaving = ref(false) + const save = async () => { + if (isSaving.value) { + return + } + isSaving.value = true + try { + const tempConfig = JSON.parse(JSON.stringify(unref(config))) + await Setting.getInstance().save(tempConfig) + isSaving.value = false + rawConfig = JSON.parse(JSON.stringify(tempConfig)) + config.value = tempConfig + } catch (error) { + isSaving.value = false + throw error + } + } + return { + config, + isSame, + isSaving, + diffKeys, + reset, + save, + } + }, + { + persist: false, + }, +) diff --git a/src/common/event/Setting/index.ts b/src/common/event/Setting/index.ts new file mode 100644 index 0000000..7abe0cc --- /dev/null +++ b/src/common/event/Setting/index.ts @@ -0,0 +1,23 @@ +import { ApiFactory } from "common/lib/abstract" +import { BaseSingleton } from "base" +import { IConfig } from "config" + +class Setting extends BaseSingleton { + constructor() { + super() + } + + private get api() { + return ApiFactory.getApiClient() + } + + sync() { + return this.api.callSync("SettingCommand.sync") + } + + save(config: IConfig) { + return this.api.call("SettingCommand.save", config) + } +} + +export { Setting } diff --git a/src/common/event/Setting/main/command.ts b/src/common/event/Setting/main/command.ts new file mode 100644 index 0000000..6c9afd6 --- /dev/null +++ b/src/common/event/Setting/main/command.ts @@ -0,0 +1,13 @@ +import Setting, { IConfig } from "setting/main" + +export default class SettingCommand { + static init() { + console.log("SettingCommand init") + } + sync() { + return Setting.config() + } + save(config: IConfig) { + return Setting.set(config) + } +} diff --git a/src/common/event/Updater/hook.ts b/src/common/event/Updater/hook.ts new file mode 100644 index 0000000..0cfc34a --- /dev/null +++ b/src/common/event/Updater/hook.ts @@ -0,0 +1,16 @@ +import { EventMaps } from "helper/updater/common" +import { defineStore } from "pinia" + +export const useSettingStore = defineStore( + "Updater", + () => { + getApi().on("update-progress", (_, data) => { + console.log(data) + }) + + return {} + }, + { + persist: false, + }, +) diff --git a/src/common/event/Updater/index.ts b/src/common/event/Updater/index.ts index 6a829d0..2b23ee4 100644 --- a/src/common/event/Updater/index.ts +++ b/src/common/event/Updater/index.ts @@ -1,15 +1,13 @@ -import { EventMaps } from "helper/updater/common" +import { BaseEvent } from "common/lib/abstract" -const curProgress = ref(0) - -getApi().on("update-progress", (_, data) => { - console.log(data) -}) +class Updater extends BaseEvent { + constructor() { + super() + } -function useUpdate() { - return { - curProgress, + test() { + this.api } } -export { useUpdate } +export { Updater } diff --git a/src/common/lib/abstract.ts b/src/common/lib/abstract.ts index 0a025d9..981d02e 100644 --- a/src/common/lib/abstract.ts +++ b/src/common/lib/abstract.ts @@ -1,9 +1,11 @@ import { ElectronApiClient } from "common/lib/electron" import { BrowserApiClient } from "common/lib/browser" +import { BaseSingleton } from "base/index" // 定义抽象 API 接口 export interface IApiClient { call(command: string, ...args: any[]): Promise + callSync(command: string, ...args: any[]): void on(channel: K, callback: (...args: any[]) => void): void off(channel: K, callback: (...args: any[]) => void): void offAll(channel: K): void @@ -15,6 +17,11 @@ class NullApiClient implements IApiClient { console.warn(`API call to ${command} failed: API client not initialized`) return undefined as any } + callSync(command: string, ...args: any[]): void { + args + console.warn(`API callSync to ${command} failed: API client not initialized`) + return undefined as any + } on(channel: K, callback: (...args: any[]) => void): void { callback @@ -51,3 +58,13 @@ export class ApiFactory { return this.instance } } + +export class BaseEvent extends BaseSingleton { + constructor() { + super() + } + + public get api() { + return ApiFactory.getApiClient() + } +} diff --git a/src/common/lib/browser.ts b/src/common/lib/browser.ts index e80a478..035a71b 100644 --- a/src/common/lib/browser.ts +++ b/src/common/lib/browser.ts @@ -11,6 +11,11 @@ export class BrowserApiClient implements IApiClient { }).then(res => res.json()) } + callSync(): void { + // 浏览器特定实现,可能使用 fetch 或其他方式 + console.log("不支持 callSync 方法") + } + // 实现其他方法... on(channel: K, callback: (...args: any[]) => void): void { // 浏览器中可能使用 WebSocket 或其他方式 diff --git a/src/common/lib/electron.ts b/src/common/lib/electron.ts index 43da684..05609d2 100644 --- a/src/common/lib/electron.ts +++ b/src/common/lib/electron.ts @@ -6,6 +6,11 @@ export class ElectronApiClient implements IApiClient { return window.api.call(command, ...args) } + callSync(command: string, ...args: any[]): Promise { + // Electron 特定实现 + return window.api.callSync(command, ...args) + } + on(channel: K, callback: (...args: any[]) => void): void { window.api.on(channel, callback) } diff --git a/src/main/modules/commands/index.ts b/src/main/modules/commands/index.ts index 45474a7..c7f97df 100644 --- a/src/main/modules/commands/index.ts +++ b/src/main/modules/commands/index.ts @@ -3,8 +3,8 @@ import { ipcMain, Menu, MenuItem } from "electron" import { inject } from "inversify" import IOC from "main/_ioc" import BaseClass from "main/base/base" -import { isPromise } from "main/utils" import WindowManager from "../window-manager" +import { isPromise } from "utils" export default class Commands extends BaseClass { destroy() { diff --git a/src/main/modules/window-manager/windowsMap.ts b/src/main/modules/window-manager/windowsMap.ts index c128cf7..4d1ca6e 100644 --- a/src/main/modules/window-manager/windowsMap.ts +++ b/src/main/modules/window-manager/windowsMap.ts @@ -1,7 +1,7 @@ import Config from "config" import { BrowserWindowConstructorOptions } from "electron" -import { getFileUrl, getPreloadUrl } from "main/utils" import icon from "@res/icon.png?asset" +import { getFileUrl, getPreloadUrl } from "utils/main" export type Param = Partial & Required> diff --git a/src/main/utils/index.ts b/src/main/utils/index.ts deleted file mode 100644 index 3d7d89d..0000000 --- a/src/main/utils/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { is } from "@electron-toolkit/utils" -import { join } from "node:path" - -export function getFileUrl(app: string) { - let winURL = "" - if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { - winURL = process.env["ELECTRON_RENDERER_URL"] + `/${app}#/` - } else { - winURL = join(__dirname, `../renderer/${app}#/`) - } - return slash(winURL) -} - -export function getPreloadUrl(file) { - return join(__dirname, `../preload/${file}.mjs`) -} - -export function isPromise(value: () => any) { - return value && Object.prototype.toString.call(value) === "[object Promise]" -} - -export function slash(path: string) { - const isExtendedLengthPath = path.startsWith("\\\\?\\") - if (isExtendedLengthPath) { - return path - } - return path.replace(/\\/g, "/") -} diff --git a/src/renderer/src/pages/index/index.vue b/src/renderer/src/pages/index/index.vue index 798684c..a55ca47 100644 --- a/src/renderer/src/pages/index/index.vue +++ b/src/renderer/src/pages/index/index.vue @@ -1,9 +1,7 @@ diff --git a/src/renderer/src/pages/setting/index.vue b/src/renderer/src/pages/setting/index.vue new file mode 100644 index 0000000..0a34780 --- /dev/null +++ b/src/renderer/src/pages/setting/index.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index 722243b..9922464 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -1,11 +1,8 @@ - -import { createPinia } from 'pinia' -import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' +import { createPinia } from "pinia" +import piniaPluginPersistedstate from "pinia-plugin-persistedstate" const pinia = createPinia() pinia.use(piniaPluginPersistedstate) export default pinia -export { - pinia -} \ No newline at end of file +export { pinia } diff --git a/src/renderer/src/store/module.store.ts b/src/renderer/src/store/module.store.ts deleted file mode 100644 index 6022e26..0000000 --- a/src/renderer/src/store/module.store.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { defineStore } from "pinia" - -export enum ModuleType { - CommonPanel = 0, - CodeMgr = 1, - LinkBox = 2, -} -export const useModuleStore = defineStore("module", () => { - const curModuleId = ref(ModuleType.CommonPanel) - - const modules: Record<"id" | "label", string | ModuleType>[] = [ - { id: ModuleType.CommonPanel, label: "全能面板" }, - { id: ModuleType.CodeMgr, label: "代码管家" }, - { id: ModuleType.LinkBox, label: "超链鉴宝" }, - ] - - const setModule = (type: ModuleType) => { - console.log(type); - curModuleId.value = type - } - const curModule = computed(() => { - return modules.find(m => m.id === curModuleId.value) - }) - return { - ModuleType, - setModule, - modules, - curModule, - } -}) diff --git a/src/renderer/src/ui/NavBar.vue b/src/renderer/src/ui/NavBar.vue index 0e487ca..61589d4 100644 --- a/src/renderer/src/ui/NavBar.vue +++ b/src/renderer/src/ui/NavBar.vue @@ -15,9 +15,7 @@
{{ Config.ExeConfig.name }}
-
{{ t("browser.navbar.menu.label") }}
-
{{ ModuleStore.curModule?.label ?? "选择模块" }}
-
设置
+
{{ menu.label }}
@@ -61,7 +59,6 @@ import { PopupMenu } from "@/bridge/PopupMenu" import { usePlatForm } from "common/event/PlatForm/hook" import { LogLevel } from "logger/common" - import { useModuleStore } from "@/store/module.store" const PlatForm = usePlatForm() @@ -82,76 +79,77 @@ return false }) - // function backHome() { - // router.push("/") - // } function back() { router.back() } const { t } = useI18n() - const onClickMenu = async e => { - const menu = new PopupMenu([ - { - label: isFullScreen.value ? t("browser.navbar.menu.quit-fullscreen") : t("browser.navbar.menu.fullscreen"), - async click() { - await PlatForm.toggleFullScreen() - isFullScreen.value = !isFullScreen.value - }, + const menuList = [ + { + label: t("browser.navbar.menu.label"), + async click(e) { + const menu = new PopupMenu([ + { + label: "首选项", + async click() { + router.push("/setting") + }, + }, + { + label: t("browser.navbar.menu.toggleDevTools"), + async click() { + PlatForm.toggleDevTools() + }, + }, + { + label: "重载", + async click() { + PlatForm.reload() + }, + }, + { + label: "崩溃", + async click() { + PlatForm.crash() + }, + }, + { + label: curLogLevel.value === LogLevel.TRACE ? "关闭调试模式" : "开启调试模式", + async click() { + if (curLogLevel.value === LogLevel.TRACE) { + await PlatForm.logSetLevel(LogLevel.INFO) + curLogLevel.value = LogLevel.INFO + return + } + await PlatForm.logSetLevel(LogLevel.TRACE) + curLogLevel.value = LogLevel.TRACE + }, + }, + ]) + const obj = e.target.getBoundingClientRect() + menu.show({ x: ~~obj.x, y: ~~(obj.y + obj.height) }) }, - { - label: t("browser.navbar.menu.toggleDevTools"), - async click() { - PlatForm.toggleDevTools() - }, + }, + { + label: "查看", + async click(e) { + const menu = new PopupMenu([ + { + label: isFullScreen.value ? t("browser.navbar.menu.quit-fullscreen") : t("browser.navbar.menu.fullscreen"), + async click() { + await PlatForm.toggleFullScreen() + isFullScreen.value = !isFullScreen.value + }, + }, + ]) + const obj = e.target.getBoundingClientRect() + menu.show({ x: ~~obj.x, y: ~~(obj.y + obj.height) }) }, - { - label: "重载", - async click() { - PlatForm.reload() - }, - }, - { - label: "崩溃", - async click() { - PlatForm.crash() - }, - }, - { - label: curLogLevel.value === LogLevel.TRACE ? "关闭调试模式" : "开启调试模式", - async click() { - if (curLogLevel.value === LogLevel.TRACE) { - await PlatForm.logSetLevel(LogLevel.INFO) - curLogLevel.value = LogLevel.INFO - return - } - await PlatForm.logSetLevel(LogLevel.TRACE) - curLogLevel.value = LogLevel.TRACE - }, - }, - ]) - const obj = e.target.getBoundingClientRect() - menu.show({ x: ~~obj.x, y: ~~(obj.y + obj.height) }) - } + }, + ] const onClickAbout = () => { PlatForm.showAbout() } - - const ModuleStore = useModuleStore() - - const onClickPage = async e => { - const menu = new PopupMenu(toRaw(ModuleStore.modules as any)) - menu.setClickEvent(item => { - ModuleStore.setModule(item.id) - // if (item.id === ModuleStore.ModuleType.CommonPanel) {} - }) - const obj = e.target.getBoundingClientRect() - menu.show({ x: ~~obj.x, y: ~~(obj.y + obj.height) }) - } - - const onClickSetting = () => { - router.push("/setting") - }