Browse Source

refactor(命令模块): 将命令模块从主进程迁移到通用模块,并重构相关代码

将 `BasicCommand`、`TabsCommand` 和 `UpdateCommand` 从主进程迁移到 `common/event` 目录下,统一管理命令模块。同时,重构了 `PlatForm` 模块,使其使用新的 `ApiFactory` 进行 API 调用,提升了代码的可维护性和复用性。
feat/icon
npmrun 2 weeks ago
parent
commit
7246ab2d9a
  1. 47
      .eslintrc.cjs
  2. 23
      .prettierrc
  3. 40
      config/index.ts
  4. 64
      packages/locales/index.ts
  5. 64
      packages/locales/main.ts
  6. 10
      src/common/_ioc.main.ts
  7. 13
      src/common/event/PlatForm/index.ts
  8. 6
      src/common/event/PlatForm/main/command.ts
  9. 0
      src/common/event/Tabs/main/command.ts
  10. 8
      src/common/event/update/index.ts
  11. 2
      src/common/event/update/main/command.ts
  12. 2
      src/common/event/update/main/index.ts
  13. 53
      src/common/lib/abstract.ts
  14. 29
      src/common/lib/browser.ts
  15. 20
      src/common/lib/electron.ts
  16. 4
      src/main/App.ts
  17. 2
      src/main/_ioc.ts
  18. 12
      src/main/commands/SettingCommand.ts
  19. 15
      src/renderer/src/main.ts
  20. 1
      tsconfig.node.json
  21. 1
      tsconfig.web.json

47
.eslintrc.cjs

@ -1,34 +1,23 @@
const { readFileSync, readFile } = require("node:fs")
/* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution")
const prettierConfig = JSON.parse(readFileSync("./.prettierrc.json", { encoding: "utf-8" }))
module.exports = {
extends: [
"eslint:recommended",
"plugin:vue/vue3-recommended",
"@electron-toolkit",
"@electron-toolkit/eslint-config-ts/eslint-recommended",
"@vue/eslint-config-typescript/recommended",
"@vue/eslint-config-prettier",
],
rules: {
"vue/require-default-prop": "off",
"vue/multi-word-component-names": "off",
"@typescript-eslint/no-explicit-any": "off",
"prettier/prettier": [
"error",
{
tabWidth: 4,
useTabs: false,
semi: false,
singleQuote: false,
trailingComma: "all",
bracketSpacing: true,
arrowParens: "avoid",
printWidth: 140,
htmlWhitespaceSensitivity: "ignore",
proseWrap: "preserve",
endOfLine: "auto",
},
],
},
extends: [
"eslint:recommended",
"plugin:vue/vue3-recommended",
"@electron-toolkit",
"@electron-toolkit/eslint-config-ts/eslint-recommended",
"@vue/eslint-config-typescript/recommended",
"@vue/eslint-config-prettier",
],
rules: {
"vue/require-default-prop": "off",
"vue/multi-word-component-names": "off",
"@typescript-eslint/no-explicit-any": "off",
"prettier/prettier": ["error", prettierConfig],
},
}

23
.prettierrc

@ -1,5 +1,5 @@
{
"tabWidth": 4,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": false,
@ -9,5 +9,24 @@
"printWidth": 140,
"htmlWhitespaceSensitivity": "ignore",
"proseWrap": "preserve",
"endOfLine": "auto"
"endOfLine": "auto",
"vueIndentScriptAndStyle": true,
"embeddedLanguageFormatting": "auto",
"jsxSingleQuote": false,
"jsxBracketSameLine": false,
"quoteProps": "as-needed",
"overrides": [
{
"files": "*.json",
"options": {
"printWidth": 80
}
},
{
"files": ["*.vue", "*.tsx"],
"options": {
"singleAttributePerLine": false
}
}
]
}

40
config/index.ts

@ -1,19 +1,31 @@
// 定义主题类型
type ThemeType = "light" | "dark" | "auto"
// 定义语言类型
type LanguageType = "zh" | "en"
// 定义编辑器logo类型
type LogoType = "logo" | "bg"
// 配置接口定义
interface IDefaultConfig {
language: LanguageType
"common.theme": ThemeType
"desktop:wallpaper": string
"update.repo"?: string
"update.owner"?: string
"update.allowDowngrade": boolean
"update.allowPrerelease": boolean
"editor.bg": string
"editor.logoType": LogoType
"editor.fontFamily": string
storagePath: string
}
interface IConfig {
app_title: string
default_config: {
language: "zh" | "en"
"common.theme": "light" | "dark" | "auto"
"desktop:wallpaper": string
"update.repo"?: string
"update.owner"?: string
"update.allowDowngrade": boolean
"update.allowPrerelease": boolean
"editor.bg": string
"editor.logoType": "logo" | "bg"
"editor.fontFamily": string
storagePath: string
}
default_config: IDefaultConfig
}
// 默认配置导出
export default {
app_title: "zephyr", // 和风
default_config: {
@ -29,4 +41,4 @@ export default {
"update.allowDowngrade": false,
"update.allowPrerelease": false,
},
} as IConfig
} as const satisfies IConfig

64
packages/locales/index.ts

@ -1,41 +1,41 @@
if (import.meta.env.DEV) {
// 引入之后可以热更新
import("./languages/zh.json")
import("./languages/en.json")
// 引入之后可以热更新
import("./languages/zh.json")
import("./languages/en.json")
}
const datetimeFormats = {
en: {
short: {
year: "numeric",
month: "short",
day: "numeric",
},
long: {
year: "numeric",
month: "short",
day: "numeric",
weekday: "short",
hour: "numeric",
minute: "numeric",
},
en: {
short: {
year: "numeric",
month: "short",
day: "numeric",
},
zh: {
short: {
year: "numeric",
month: "short",
day: "numeric",
},
long: {
year: "numeric",
month: "short",
day: "numeric",
weekday: "short",
hour: "numeric",
minute: "numeric",
hour12: true,
},
long: {
year: "numeric",
month: "short",
day: "numeric",
weekday: "short",
hour: "numeric",
minute: "numeric",
},
},
zh: {
short: {
year: "numeric",
month: "short",
day: "numeric",
},
long: {
year: "numeric",
month: "short",
day: "numeric",
weekday: "short",
hour: "numeric",
minute: "numeric",
hour12: true,
},
},
}
export { datetimeFormats }

64
packages/locales/main.ts

@ -5,47 +5,47 @@ import zh from "./languages/zh.json"
import en from "./languages/en.json"
type FlattenObject<T, Prefix extends string = ""> = T extends object
? {
[K in keyof T & (string | number)]: FlattenObject<T[K], Prefix extends "" ? `${K}` : `${Prefix}.${K}`>
}[keyof T & (string | number)]
: Prefix
? {
[K in keyof T & (string | number)]: FlattenObject<T[K], Prefix extends "" ? `${K}` : `${Prefix}.${K}`>
}[keyof T & (string | number)]
: Prefix
type FlattenKeys<T> = FlattenObject<T>
type TranslationKey = FlattenKeys<typeof zh>
class Locale {
locale: string = "zh"
constructor() {
try {
this.locale = app.getLocale()
} catch (e) {
console.log(e)
}
}
locale: string = "zh"
isCN(): boolean {
return this.locale.startsWith("zh")
constructor() {
try {
this.locale = app.getLocale()
} catch (e) {
console.log(e)
}
t(key: TranslationKey, replacements?: Record<string, string>): string {
let text: string = this.isCN() ? get(zh, key) : get(en, key)
if (!text) {
text = get(zh, key)
if (!text) {
return key
}
}
if (replacements) {
// 替换所有形如 {key} 的占位符
Object.entries(replacements).forEach(([key, value]) => {
console.log(text)
text = text.replace(new RegExp(`{${key}}`, "g"), value)
})
}
return text
}
isCN(): boolean {
return this.locale.startsWith("zh")
}
t(key: TranslationKey, replacements?: Record<string, string>): string {
let text: string = this.isCN() ? get(zh, key) : get(en, key)
if (!text) {
text = get(zh, key)
if (!text) {
return key
}
}
if (replacements) {
// 替换所有形如 {key} 的占位符
Object.entries(replacements).forEach(([key, value]) => {
console.log(text)
text = text.replace(new RegExp(`{${key}}`, "g"), value)
})
}
return text
}
}
const Locales = new Locale()

10
src/main/commands/_ioc.ts → src/common/_ioc.main.ts

@ -1,13 +1,11 @@
import { Container, ContainerModule } from "inversify"
import BasicCommand from "./BasicCommand"
import TabsCommand from "./TabsCommand"
import UpdateCommand from "./UpdateCommand"
// TODO 考虑迁移,将所有命令都注册common/event中
import UpdateCommand from "common/event/Update/main/command"
import PlatFormCommand from "common/event/PlatForm/main/command"
import TabsCommand from "common/event/Tabs/main/command"
const modules = new ContainerModule(bind => {
bind("BasicCommand").to(BasicCommand).inSingletonScope()
bind("TabsCommand").to(TabsCommand).inSingletonScope()
bind("PlatFormCommand").to(PlatFormCommand).inSingletonScope()
bind("UpdateCommand").to(UpdateCommand).inSingletonScope()
})

13
src/common/event/PlatForm/index.ts

@ -1,24 +1,29 @@
import { _Base } from "common/lib/_Base"
import { ApiFactory } from "common/lib/abstract"
class PlatForm extends _Base {
constructor() {
super()
}
private get api() {
return ApiFactory.getApiClient()
}
async showAbout() {
return await fetch("api://fuck/BasicService/showAbout")
return this.api.call("BasicService.showAbout")
}
async isFullScreen() {
return await api.call("BasicCommand.isFullscreen")
return this.api.call("PlatFormCommand.isFullscreen")
}
async toggleFullScreen() {
return api.call("BasicCommand.fullscreen")
return this.api.call("PlatFormCommand.fullscreen")
}
async toggleDevTools() {
return api.call("BasicCommand.toggleDevTools")
return this.api.call("PlatFormCommand.toggleDevTools")
}
}

6
src/main/commands/BasicCommand.ts → src/common/event/PlatForm/main/command.ts

@ -3,7 +3,7 @@ import { inject } from "inversify"
import Tabs from "main/modules/tabs"
import WindowManager from "main/modules/window-manager"
export default class BasicCommand {
export default class PlatFormCommand {
constructor(
@inject(WindowManager) private _WindowManager: WindowManager,
@inject(Tabs) private _Tabs: Tabs,
@ -20,6 +20,10 @@ export default class BasicCommand {
}
}
showAbout() {
this._WindowManager.showWindow("about")
}
toggleDevTools() {
const focusedWindow = this._WindowManager.getFocusWindow()
if (focusedWindow) {

0
src/main/commands/TabsCommand.ts → src/common/event/Tabs/main/command.ts

8
src/common/event/update/index.ts

@ -1,9 +1,9 @@
import type { AllKeys } from "../common"
// import type { AllKeys } from "../common"
const curProgress = ref(0)
api.on<AllKeys>("progress", () => {
curProgress.value = 10
})
// api.on<AllKeys>("progress", () => {
// curProgress.value = 10
// })
function useUpdate() {
return {

2
src/main/commands/UpdateCommand.ts → src/common/event/update/main/command.ts

@ -1,7 +1,7 @@
import { inject } from "inversify"
import Updater from "main/modules/updater"
export default class BasicCommand {
export default class PlatFormCommand {
constructor(@inject(Updater) private _Updater: Updater) {}
async triggerHotUpdate() {

2
src/common/event/update/main.ts → src/common/event/update/main/index.ts

@ -1,5 +1,5 @@
import { broadcast } from "main/utils"
import { AllKeys } from "../common"
import { AllKeys } from "common/event/common"
function emitHotUpdateReady(...argu) {
broadcast<AllKeys>("hot-update-ready", ...argu)

53
src/common/lib/abstract.ts

@ -0,0 +1,53 @@
import { ElectronApiClient } from "common/lib/electron"
import { BrowserApiClient } from "common/lib/browser"
// 定义抽象 API 接口
export interface IApiClient {
call<T = any>(command: string, ...args: any[]): Promise<T>
on<K extends string>(channel: K, callback: (...args: any[]) => void): void
off<K extends string>(channel: K, callback: (...args: any[]) => void): void
offAll<K extends string>(channel: K): void
}
class NullApiClient implements IApiClient {
async call<T = any>(command: string, ...args: any[]): Promise<T> {
args
console.warn(`API call to ${command} failed: API client not initialized`)
return undefined as any
}
on<K extends string>(channel: K, callback: (...args: any[]) => void): void {
callback
console.warn(`Failed to register listener for ${channel}: API client not initialized`)
}
off<K extends string>(channel: K, callback: (...args: any[]) => void): void {
callback
console.warn(`Failed to unregister listener for ${channel}: API client not initialized`)
}
offAll<K extends string>(channel: K): void {
console.warn(`Failed to unregister all listeners for ${channel}: API client not initialized`)
}
}
// 创建 API 工厂
export class ApiFactory {
private static instance: IApiClient = new NullApiClient() // 默认使用空实现
static setApiClient(client: IApiClient) {
this.instance = client
}
static getApiClient(): IApiClient {
if (this.instance instanceof NullApiClient) {
// 根据环境选择合适的 API 客户端
if (window.api && window.electron) {
this.instance = new ElectronApiClient()
} else {
this.instance = new BrowserApiClient()
}
}
return this.instance
}
}

29
src/common/lib/browser.ts

@ -0,0 +1,29 @@
import { IApiClient } from "./abstract"
export class BrowserApiClient implements IApiClient {
call<T = any>(command: string, ...args: any[]): Promise<T> {
// 浏览器特定实现,可能使用 fetch 或其他方式
const [service, method] = command.split(".")
return fetch(`/api/${service}/${method}`, {
method: "POST",
body: JSON.stringify(args),
headers: { "Content-Type": "application/json" },
}).then(res => res.json())
}
// 实现其他方法...
on<K extends string>(channel: K, callback: (...args: any[]) => void): void {
// 浏览器中可能使用 WebSocket 或其他方式
console.log("不支持 on 方法", channel, callback)
}
off<K extends string>(channel: K, callback: (...args: any[]) => void): void {
// 相应的解绑实现
console.log("不支持 on 方法", channel, callback)
}
offAll<K extends string>(channel: K): void {
// 相应的全部解绑实现
console.log("不支持 on 方法", channel)
}
}

20
src/common/lib/electron.ts

@ -0,0 +1,20 @@
import { IApiClient } from "./abstract"
export class ElectronApiClient implements IApiClient {
call<T = any>(command: string, ...args: any[]): Promise<T> {
// Electron 特定实现
return window.api.call(command, ...args)
}
on<K extends string>(channel: K, callback: (...args: any[]) => void): void {
window.api.on(channel, callback)
}
off<K extends string>(channel: K, callback: (...args: any[]) => void): void {
window.api.off(channel, callback)
}
offAll<K extends string>(channel: K): void {
window.api.offAll(channel)
}
}

4
src/main/App.ts

@ -70,8 +70,8 @@ class App extends BaseClass {
this._Zephyr.init()
electronApp.setAppUserModelId("top.xieyaxin")
this._WindowManager.showMainWindow()
this._Command.invoke("BasicCommand.setTheme", "light")
this._Command.invoke("BasicCommand.setTitlBar", {
this._Command.invoke("PlatFormCommand.setTheme", "light")
this._Command.invoke("PlatFormCommand.setTitlBar", {
height: 29,
color: "#F8F8F8",
symbolColor: "#000000",

2
src/main/_ioc.ts

@ -2,7 +2,7 @@ import IOC from "./_iocClass"
import { Container } from "inversify"
import iocModules, { destroyAllModules } from "./modules/_ioc"
import iocController, { destroyAllController } from "./controller/_ioc"
import iocCommand, { destroyAllCommand } from "./commands/_ioc"
import iocCommand, { destroyAllCommand } from "common/_ioc.main"
import App from "./App"
async function destroyAll() {

12
src/main/commands/SettingCommand.ts

@ -1,12 +0,0 @@
// import { inject } from "inversify"
// import Setting from "main/modules/setting"
// class SettingCommand {
// constructor(@inject(Setting) private _Setting: Setting) {
// console.log(this._Setting)
// }
// getAll() {
// return this._Setting.config()
// }
// }

15
src/renderer/src/main.ts

@ -10,6 +10,19 @@ import router from "./router"
import i18n from "./i18n"
const app = createApp(App)
// 全局错误处理
app.config.errorHandler = (err, instance, info) => {
console.error("应用错误:", err)
console.info("错误信息:", info)
// 可以添加错误上报逻辑
}
// 开发环境下的性能监控
if (import.meta.env.DEV) {
app.config.performance = true
}
app.use(i18n)
app.use(router as any)
app.use(router)
app.mount("#app")

1
tsconfig.node.json

@ -8,6 +8,7 @@
"src/types/**/*",
"packages/locales/main.ts",
"src/common/**/*.main.ts",
"src/common/**/main/**/*",
"src/common/**/main.ts",
"src/common/**/*.common.ts",
"src/common/**/common.ts"

1
tsconfig.web.json

@ -15,6 +15,7 @@
],
"exclude": [
"packages/locales/main.ts",
"src/common/**/main/**/*",
"src/common/**/*.main.ts",
"src/common/**/main.ts"
],

Loading…
Cancel
Save