谢亚昕 2 weeks ago
parent
commit
403e6608d3
  1. 113
      .github/workflows/Build.yml
  2. 0
      backup/Tabs/index.ts
  3. 0
      backup/Tabs/main/command.ts
  4. 3
      changelog/0.0.1.md
  5. 26
      packages/base/event/main/index.ts
  6. 0
      packages/helper/db/custom.ts
  7. 20
      packages/helper/db/index.ts
  8. 14
      packages/helper/updater/common.ts
  9. 91
      packages/helper/updater/main/index.ts
  10. 3
      packages/locales/main.ts
  11. 3
      packages/logger/main.ts
  12. 3
      packages/logger/renderer-error.ts
  13. 3
      packages/setting/main.ts
  14. 1
      packages/setting/main/event.ts
  15. 12
      packages/utils/main/session/index.ts
  16. 16
      src/common/_ioc.main.ts
  17. 38
      src/common/event/PlatForm/hook.ts
  18. 7
      src/common/event/PlatForm/main/command.ts
  19. 37
      src/common/event/Updater/hook.ts
  20. 8
      src/common/event/Updater/main/command.ts
  21. 3
      src/main/App.ts
  22. 6
      src/main/controller/BasicService.ts
  23. 9
      src/main/modules/_ioc.ts
  24. 3
      src/preload/index.ts
  25. 2
      src/renderer/components.d.ts
  26. 1
      src/renderer/src/App.vue
  27. 20
      src/renderer/src/pages/index/index.vue
  28. 52
      src/renderer/src/ui/NavBar.vue
  29. 14
      src/types/global.d.ts
  30. 3
      temp/dev.yml

113
.github/workflows/Build.yml

@ -0,0 +1,113 @@
# https://zhuanlan.zhihu.com/p/164901026
# https://www.antmoe.com/posts/18c087cf/
# https://zhuanlan.zhihu.com/p/348712087
# https://cloud.tencent.com/developer/article/1949574
# 此工作流的名字
name: Build
# 工作流的执行时机,可以设定为定时执行,每次push后执行,手动执行等
on:
# workflow_dispatch为在Github仓库的Actions面板中手动执行
# workflow_dispatch:
push:
branches:
- master
# 工作/任务,这里的工作是可以并行的。
jobs:
# 工作的名称“编译windows版”
build:
# 运行的操作系统 windows
runs-on: ${{ matrix.os }}
env:
GH_TOKEN: ${{ secrets.ELECTRON_TOKEN }}
IS_ACTIONS: true
strategy:
matrix:
# https://www.likecs.com/ask-314443.html
node-version: [18.17.1]
os: [windows-2022, ubuntu-latest]
# 步骤
steps:
# 使用预制action:拉取最新的代码
- uses: actions/checkout@v3
with:
ref: master
# https://pnpm.io/zh/continuous-integration/#github-actions
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
id: pnpm-install
with:
version: 8.7.6
# 安装node
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
# 安装python
- name: Use Python 3.9.13
uses: actions/setup-python@v4
with:
python-version: 3.9.13
env:
PYTHON_VERSION: 3.9.13
# https://docs.microsoft.com/zh-cn/visualstudio/releases/2017/vs2017-system-requirements-vs
# 将windows设置成windows-2016,2016要取消支持了,可换成2022
# - name: set msvs
# run: npm config set msvs_version 2022
# https://github.com/wxWidgets/wxWidgets/blob/master/.github/workflows/ci_msw.yml
# https://github.com/microsoft/setup-msbuild
- name: Add msbuild to PATH
if: matrix.os == 'windows-2022'
uses: microsoft/setup-msbuild@v1.1
with:
vs-prerelease: true
# 步骤一的名称:
- name: Build
# 该步骤运行的终端命令,运行编译命令
run: npm run build
# 步骤二的名称:将编译后的结果上传
# - name: Upload File
# # 使用预制action:上传文件,可以将执行路径打包成zip上传
# uses: actions/upload-artifact@v3
# with:
# # 上传后文件的名称
# name: windows
# # 打包的路径以及文件过滤,此为仅打包dist目录下的exe文件
# path: out/*exe
- name: 读取当前版本号
id: version
uses: ashley-taylor/read-json-property-action@v1.1
with:
path: ./dist/package.json
property: version
- name: 读取描述文件
id: description
uses: juliangruber/read-file-action@v1
with:
path: ./changelog/${{steps.version.outputs.value}}.md
# step5: cleanup artifacts in dist_electron
# - name: 清理不必要的资产
# run: |
# npx rimraf "out/!(*.exe|*.dmg)"
# - name: Generate release tag
# id: tag
# run: |
# echo "::set-output name=release_tag::UserBuild_$(date +"%Y.%m.%d_%H-%M")"
- name: Generate release tag
id: tag
run: |
echo "::set-output name=release_tag::v${{steps.version.outputs.value}}"
# echo "release_tag=v${{steps.version.outputs.value}}" >> $GITHUB_OUTPUT
- name: release # https://github.com/softprops/action-gh-release/issues/20
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.tag.outputs.release_tag }}
name: ${{ steps.tag.outputs.release_tag }}
files: "out/*exe,out/*dmg,out/*AppImage,out/*yml"
body: ${{steps.description.outputs.content}}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.ELECTRON_TOKEN }}

0
src/common/event/Tabs/index.ts → backup/Tabs/index.ts

0
src/common/event/Tabs/main/command.ts → backup/Tabs/main/command.ts

3
changelog/0.0.1.md

@ -0,0 +1,3 @@
## 0.0.1
第一个版本

26
packages/base/event/main/index.ts

@ -8,17 +8,31 @@ class FireEvent<T extends Record<string | symbol, FireFN>> {
console.log(`${key}: ${this.#events[key]}\n`)
})
}
on<S extends keyof T>(name: S, fn: T[S]) {
on<S extends keyof T | "*">(name: S, fn: S extends "*" ? (...argus: [keyof T, T[keyof T]]) => void : T[S]) {
if (!this.#events[name]) {
this.#events[name] = []
}
this.#events[name].push(fn)
this.#events[name].push(fn as any)
}
emit<S extends keyof T>(name: S, ...argu: Parameters<T[S]>) {
async emit<S extends keyof T>(name: S, ...argu: Parameters<T[S]>) {
if (this.#events[name]) {
this.#events[name].forEach(fn => {
fn(...argu)
})
const returnValues: any = []
for (let i = 0; i < this.#events[name].length; i++) {
const fn = this.#events[name][i]
let r
if (Object.prototype.toString.call(fn) === "[object AsyncFunction]") {
r = await fn(...argu)
} else {
r = fn(...argu)
}
returnValues.push(r)
}
}
if (this.#events["*"]) {
for (let i = 0; i < this.#events["*"].length; i++) {
const fn = this.#events["*"][i]
fn(name, ...argu)
}
}
}
off<S extends keyof T>(name: S, fn?: T[S]) {

0
src/main/modules/db/custom.ts → packages/helper/db/custom.ts

20
src/main/modules/db/index.ts → packages/helper/db/index.ts

@ -1,27 +1,13 @@
import { injectable } from "inversify"
import Setting from "setting/main"
import { CustomAdapter, CustomLow } from "./custom"
import path from "node:path"
import BaseClass from "main/base/base"
import _logger from "logger/main"
// import _logger from "logger/main"
const logger = _logger.createNamespace("db")
// const logger = _logger.createNamespace("db")
@injectable()
class DB extends BaseClass {
destroy() {
logger.debug(`DB destroy`)
}
class DB {
Modules: Record<string, CustomLow<any>> = {}
constructor() {
super()
}
init() {
console.log("DB Init")
}
create(filepath) {
const adapter = new CustomAdapter<any>(filepath)
const db = new CustomLow<object>(adapter, {})

14
packages/helper/updater/common.ts

@ -1,3 +1,15 @@
import { UpdateInfo } from "electron-updater"
export interface UpdaterCommand {
checkForUpdates: () => void
}
export type EventMaps = {
"update-progress": (data: { percent: number; all: number; now: number }) => void
"update-progress": (data: { speed: number; percent: number; all: number; now: number }) => void
error: (err: any) => void
"updater:error": (info: UpdateInfo) => void
"checking-for-update": () => void
"update-available": (info: UpdateInfo) => void
"update-not-available": (info: UpdateInfo) => void
"updater:downloaded": (p: any) => void
}

91
packages/helper/updater/main/index.ts

@ -1,17 +1,19 @@
import pkg from "electron-updater"
import { app, dialog } from "electron"
import { NsisUpdater } from "electron-updater"
import { app, BrowserWindow, dialog } from "electron"
import { is } from "@electron-toolkit/utils"
import Setting from "setting/main"
import { BaseSingleton } from "base"
import { fetchHotUpdatePackage, flagNeedUpdate } from "./hot"
import Locales from "locales/main"
import _logger from "logger/main"
import { buildEmitter } from "base/event/main"
import { EventMaps } from "../common"
import path from "path"
const logger = _logger.createNamespace("updater")
const { autoUpdater } = pkg
class _Updater extends BaseSingleton {
public events = buildEmitter()
public events = buildEmitter<EventMaps>()
private timer: ReturnType<typeof setInterval> | null = null
// autoReplace = false
async triggerHotUpdate(autoReplace = false) {
@ -28,42 +30,83 @@ class _Updater extends BaseSingleton {
}
}
updateInfo: ConstructorParameters<typeof NsisUpdater>[0]
autoUpdater: NsisUpdater
constructor() {
super()
this.autoUpdater = new NsisUpdater({
provider: "github",
owner: "npmrun",
repo: "electron-app",
})
Setting.onChange("update.allowDowngrade", () => {
this.autoUpdater.allowDowngrade = Setting.values("update.allowDowngrade")
})
Setting.onChange("update.allowPrerelease", () => {
this.autoUpdater.allowPrerelease = Setting.values("update.allowPrerelease")
})
Setting.onChange(["update.owner", "update.repo"], () => {
this.autoUpdater.setFeedURL({
provider: "github",
owner: "npmrun",
repo: "electron-app",
})
})
// 配置自动更新
autoUpdater.autoDownload = false
autoUpdater.autoInstallOnAppQuit = true
this.autoUpdater.autoDownload = false
this.autoUpdater.autoInstallOnAppQuit = true
this.autoUpdater.allowPrerelease = true
this.autoUpdater.allowDowngrade = true
this.autoUpdater.forceDevUpdateConfig = is.dev
if (is.dev) {
this.autoUpdater.updateConfigPath = path.resolve(process.cwd(), "temp/dev.yml")
}
// 检查更新错误
autoUpdater.on("error", error => {
this.autoUpdater.on("error", error => {
logger.debug("Update error:", error)
this.events.emit("error", error)
})
// 检查更新
autoUpdater.on("checking-for-update", () => {
this.autoUpdater.on("checking-for-update", () => {
logger.debug("Checking for updates...")
this.events.emit("checking-for-update")
})
// 有可用更新
autoUpdater.on("update-available", info => {
this.autoUpdater.on("update-available", info => {
logger.debug("Update available:", info)
this.events.emit("update-available", info)
this.promptUserToUpdate()
})
// 没有可用更新
autoUpdater.on("update-not-available", info => {
this.autoUpdater.on("update-not-available", info => {
logger.debug("Update not available:", info)
this.events.emit("update-not-available", info)
})
// 更新下载进度
autoUpdater.on("download-progress", progressObj => {
this.autoUpdater.on("download-progress", progressObj => {
logger.debug(
`Download speed: ${progressObj.bytesPerSecond} - Downloaded ${progressObj.percent}% (${progressObj.transferred}/${progressObj.total})`,
)
this.events.emit("update-progress", {
speed: progressObj.bytesPerSecond,
percent: progressObj.percent,
all: progressObj.total,
now: progressObj.transferred,
})
})
// 更新下载完成
autoUpdater.on("update-downloaded", info => {
this.autoUpdater.on("update-downloaded", info => {
logger.debug("Update downloaded:", info)
this.promptUserToInstall()
})
@ -89,16 +132,12 @@ class _Updater extends BaseSingleton {
}
}
private async checkForUpdates() {
if (app.isPackaged) {
try {
await autoUpdater.checkForUpdates()
logger.debug("Updater初始化检查成功.")
} catch (error) {
logger.debug("Failed to check for updates:", error)
}
} else {
logger.debug("正在开发模式,跳过更新检查.")
async checkForUpdates() {
try {
this.autoUpdater.checkForUpdates()
logger.debug("Updater初始化检查成功.")
} catch (error) {
logger.debug("Failed to check for updates:", error)
}
}
@ -112,21 +151,21 @@ class _Updater extends BaseSingleton {
})
if (result.response === 0) {
autoUpdater.downloadUpdate()
this.autoUpdater.downloadUpdate()
}
}
private async promptUserToInstall() {
const result = await dialog.showMessageBox({
const result = await dialog.showMessageBox(BrowserWindow.getFocusedWindow()!, {
type: "info",
title: "更新已就绪",
message: "新版本已下载完成,是否立即安装?",
buttons: ["立即安装", "稍后安装"],
buttons: ["立即安装"],
defaultId: 0,
})
if (result.response === 0) {
autoUpdater.quitAndInstall(false, true)
this.autoUpdater.quitAndInstall(false, true)
}
}
}

3
packages/locales/main.ts

@ -21,7 +21,7 @@ class Locale {
try {
this.locale = app.getLocale()
} catch (e) {
console.log(e)
console.error(e)
}
}
@ -40,7 +40,6 @@ class Locale {
if (replacements) {
// 替换所有形如 {key} 的占位符
Object.entries(replacements).forEach(([key, value]) => {
console.log(text)
text = text.replace(new RegExp(`{${key}}`, "g"), value)
})
}

3
packages/logger/main.ts

@ -260,6 +260,9 @@ export class Logger {
// 默认实例
const logger = Logger.getInstance()
logger.init()
emitter.on("init", setting => {
logger.setLevel(setting["dev:debug"])
})
emitter.on("update", setting => {
logger.setLevel(setting["dev:debug"])
})

3
packages/logger/renderer-error.ts

@ -107,7 +107,7 @@ const formatError = (error: any, options: ErrorHandlerOptions): ErrorDetail => {
return errorDetail
}
// @ts-ignore
// @ts-ignore 忽略preload层不存在的问题
const preloadErrorHandler = window.preloadErrorHandler
/**
@ -223,6 +223,7 @@ const errorHandler = createRendererErrorHandler()
// 安装全局错误处理器
errorHandler.installGlobalHandlers()
// @ts-ignore 忽略全局问题
window.errorHandler = errorHandler
/**

3
packages/setting/main.ts

@ -5,6 +5,7 @@ import { cloneDeep } from "lodash"
import Config from "config"
import type { IConfig } from "config"
import _logger from "logger/main"
import { emitter } from "./main/event"
const logger = _logger.createNamespace("setting")
@ -137,6 +138,7 @@ class SettingClass {
this.#sync()
}
init.call(this, this.#config)
emitter.emit("update", this.#config, this.#config)
}
config() {
return this.#config
@ -215,6 +217,7 @@ class SettingClass {
if (isChange) {
this.#sync()
this.#runCB(this.#config, oldMainConfig, changeKeys)
emitter.emit("update", this.#config, oldMainConfig, changeKeys)
}
}
values<T extends keyof IConfig>(key: T): IConfig[T] {

1
packages/setting/main/event.ts

@ -2,5 +2,6 @@ import { buildEmitter } from "base/event/main"
import type { IOnFunc } from "setting/main"
export const emitter = buildEmitter<{
init: IOnFunc
update: IOnFunc
}>()

12
packages/utils/main/session/index.ts

@ -11,7 +11,7 @@ function createLoginWin(partition) {
partition = partition || `persist:${Math.random()}`
// const charset = require("superagent-charset")
// const request = charset(require("superagent")) // HTTP
let presWindow = new BrowserWindow({
const presWindow = new BrowserWindow({
width: 1280,
height: 768,
title: "用户登陆",
@ -21,14 +21,14 @@ function createLoginWin(partition) {
partition,
},
})
let webContents = presWindow.webContents
return new Promise(function (resove, _) {
const webContents = presWindow.webContents
return new Promise(function (resove) {
// webContents.openDevTools();
presWindow.loadURL("https://login.taobao.com/member/login.jhtml")
webContents.on("did-navigate-in-page", async function () {
// 这里可以看情况进行参数的传递,获取制定的 cookies
const cookies = await webContents.session.cookies.get({})
let obj = { partition, cookies }
const obj = { partition, cookies }
resove(obj)
// webContents.session.cookies.get({}, function (err, cookies) {
// if (err) {
@ -77,6 +77,4 @@ function createLoginWin(partition) {
})
}
export {
createLoginWin
}
export { createLoginWin }

16
src/common/_ioc.main.ts

@ -1,4 +1,7 @@
import { Container, ContainerModule } from "inversify"
import _logger from "logger/main"
const logger = _logger.createNamespace("command")
/**
*
@ -12,6 +15,7 @@ const modules = new ContainerModule(bind => {
const CommandClass = (module as { default: any }).default
if (CommandClass) {
const className = CommandClass.name.replace("Command", "")
logger.debug(`绑定命令类: ${className}Command`)
if (CommandClass["init"]) {
CommandClass["init"]()
}
@ -27,6 +31,18 @@ const modules = new ContainerModule(bind => {
* @param ioc - Inversify
*/
async function destroyAllCommand(ioc: Container) {
const allIOC: any[] = []
Object.values(commandModules).forEach(module => {
const CommandClass = (module as { default: any }).default
if (CommandClass) {
const className = CommandClass.name.replace("Command", "")
const m = ioc.get(className + "Command") as any
if (m && m.destroy) {
allIOC.push(m.destroy())
}
}
})
await Promise.all(allIOC)
await ioc.unloadAsync(modules)
}

38
src/common/event/PlatForm/hook.ts

@ -1,5 +1,41 @@
import { LogLevel } from "logger/common"
import { PlatForm } from "."
export function usePlatForm() {
return PlatForm.getInstance<PlatForm>()
const plat = PlatForm.getInstance<PlatForm>()
// 全屏状态
const isFullScreen = ref(false)
;(async () => {
isFullScreen.value = await plat.isFullScreen()
})()
const toggleFullScreen = async () => {
await plat.toggleFullScreen()
isFullScreen.value = !isFullScreen.value
}
// 全屏状态 END
const curLogLevel = ref<LogLevel>()
;(async () => {
curLogLevel.value = await plat.logGetLevel()
})()
const isOpenDebug = computed(() => curLogLevel.value === LogLevel.TRACE)
const toggleDebugMode = async () => {
if (curLogLevel.value === LogLevel.TRACE) {
await plat.logSetLevel(LogLevel.INFO)
curLogLevel.value = LogLevel.INFO
return
}
await plat.logSetLevel(LogLevel.TRACE)
curLogLevel.value = LogLevel.TRACE
}
return {
power: plat,
isOpenDebug,
toggleDebugMode,
toggleFullScreen,
isFullScreen,
}
}

7
src/common/event/PlatForm/main/command.ts

@ -1,7 +1,6 @@
import { app, dialog, nativeTheme, TitleBarOverlayOptions } from "electron"
import { inject } from "inversify"
import errorHandler from "logger/main-error"
import Tabs from "main/modules/tabs"
import WindowManager from "main/modules/window-manager"
import icon from "@res/icon.png?asset"
import setting from "setting/main"
@ -9,10 +8,7 @@ import { LogLevel } from "logger/common"
import { getFileUrl } from "utils/main"
export default class PlatFormCommand {
constructor(
@inject(WindowManager) private _WindowManager: WindowManager,
@inject(Tabs) private _Tabs: Tabs,
) {}
constructor(@inject(WindowManager) private _WindowManager: WindowManager) {}
setTheme(theme: typeof nativeTheme.themeSource) {
nativeTheme.themeSource = theme
@ -115,7 +111,6 @@ export default class PlatFormCommand {
return
}
}
this._Tabs.closeAll()
focusedWindow!.reload()
}
}

37
src/common/event/Updater/hook.ts

@ -1,14 +1,41 @@
import { EventMaps } from "helper/updater/common"
import { EventMaps, UpdaterCommand } from "helper/updater/common"
import { defineStore } from "pinia"
export const useSettingStore = defineStore(
export const useUpdaterStore = defineStore(
"Updater",
() => {
getApi<EventMaps>().on("update-progress", (_, data) => {
const isNeedUpdate = ref(false)
const isChecking = ref(false)
const api = getApi<UpdaterCommand, EventMaps, "UpdaterCommand">()
api.on("error", (_, data) => {
isChecking.value = false
console.log(data)
})
return {}
api.on("update-not-available", (_, data) => {
isChecking.value = false
console.log(data)
})
api.on("update-available", (_, data) => {
isChecking.value = false
isNeedUpdate.value = true
console.log(data)
})
api.on("update-progress", (_, data) => {
console.log(data)
})
api.on("checking-for-update", () => {
isChecking.value = true
})
api.call("UpdaterCommand.checkForUpdates")
return {
isChecking,
isNeedUpdate,
checkForUpdates() {
if (isChecking.value) return
api.call("UpdaterCommand.checkForUpdates")
},
}
},
{
persist: false,

8
src/common/event/Updater/main/command.ts

@ -1,5 +1,6 @@
import Updater from "helper/updater/main"
import _logger from "logger/main"
import { broadcast } from "utils/main"
const logger = _logger.createNamespace("UpdaterCommand")
@ -7,9 +8,16 @@ export default class UpdaterCommand {
static init() {
// 命令初始化
logger.debug("UpdaterCommand init")
Updater.events.on("*", (name, ...argus) => {
broadcast(name, ...argus)
})
}
async triggerHotUpdate() {
Updater.triggerHotUpdate()
}
checkForUpdates() {
return Updater.checkForUpdates()
}
}

3
src/main/App.ts

@ -8,7 +8,6 @@ import { electronApp } from "@electron-toolkit/utils"
import Command from "./modules/commands"
import BaseClass from "./base/base"
import IOC from "./_ioc"
import DB from "./modules/db"
import Zephyr from "./modules/zephyr"
import { crashHandler } from "logger/crash-handler"
import logger from "logger/main"
@ -53,7 +52,6 @@ class App extends BaseClass {
@inject(IOC) private _IOC: IOC,
@inject(Api) private _Api: Api,
@inject(Command) private _Command: Command,
@inject(DB) private _DB: DB,
@inject(WindowManager) private _WindowManager: WindowManager,
@inject(Zephyr) private _Zephyr: Zephyr,
) {
@ -68,7 +66,6 @@ class App extends BaseClass {
// 开启硬件加速
app.disableHardwareAcceleration()
crashHandler.init()
this._DB.init()
this._Command.init()
this._WindowManager.init()
app.whenReady().then(() => {

6
src/main/controller/BasicService.ts

@ -1,13 +1,13 @@
import { inject, injectable } from "inversify"
import BaseContainer from "main/base/baseContainer"
import Tabs from "main/modules/tabs"
// import Tabs from "main/modules/tabs"
import WindowManager from "main/modules/window-manager"
@injectable()
class BasicService extends BaseContainer {
constructor(
@inject(WindowManager) private _WindowManager: WindowManager,
@inject(Tabs) private _Tabs: Tabs,
// @inject(Tabs) private _Tabs: Tabs,
) {
super()
}
@ -21,7 +21,7 @@ class BasicService extends BaseContainer {
openTabDevtool() {
// this._Tabs.reload(0)
this._Tabs.openDevtool(0)
// this._Tabs.openDevtool(0)
}
}

9
src/main/modules/_ioc.ts

@ -1,8 +1,7 @@
import { Container, ContainerModule } from "inversify"
import { DB } from "./db"
import { Api } from "./api"
import { WindowManager } from "./window-manager"
import { Tabs } from "./tabs"
// import { Tabs } from "./tabs"
import Commands from "./commands"
import Zephyr from "./zephyr"
@ -11,8 +10,7 @@ const modules = new ContainerModule(bind => {
bind(Api).toSelf().inSingletonScope()
bind(WindowManager).toSelf().inSingletonScope()
bind(Commands).toSelf().inSingletonScope()
bind(Tabs).toSelf().inSingletonScope()
bind(DB).toSelf().inSingletonScope()
// bind(Tabs).toSelf().inSingletonScope()
})
async function destroyAllModules(ioc: Container) {
@ -20,9 +18,8 @@ async function destroyAllModules(ioc: Container) {
ioc.get(WindowManager).destroy(),
ioc.get(Commands).destroy(),
ioc.get(Zephyr).destroy(),
ioc.get(Tabs).destroy(),
// ioc.get(Tabs).destroy(),
ioc.get(Api).destroy(),
ioc.get(DB).destroy(),
])
ioc.unloadAsync(modules)
}

3
src/preload/index.ts

@ -67,6 +67,7 @@ ipcRenderer.once("bind-window-manager", (_, name: string) => {
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld("electron", electronAPI)
contextBridge.exposeInMainWorld("getApi", () => api)
contextBridge.exposeInMainWorld("api", api)
} catch (error) {
console.error(error)
@ -76,4 +77,6 @@ if (process.contextIsolated) {
window.electron = electronAPI
// @ts-ignore (define in dts)
window.api = api
// @ts-ignore (define in dts)
window.getApi = () => api
}

2
src/renderer/components.d.ts

@ -11,8 +11,10 @@ declare module 'vue' {
AdjustLine: typeof import('./src/components/AdjustLine.vue')['default']
CodeEditor: typeof import('./src/components/CodeEditor/code-editor.vue')['default']
IconFluentCollections24Regular: typeof import('~icons/fluent/collections24-regular')['default']
'IconGrommetIcons:update': typeof import('~icons/grommet-icons/update')['default']
IconHugeiconsInbox: typeof import('~icons/hugeicons/inbox')['default']
IconMynauiTrash: typeof import('~icons/mynaui/trash')['default']
'IconQlementineIcons:update16': typeof import('~icons/qlementine-icons/update16')['default']
IconSolarHome2Outline: typeof import('~icons/solar/home2-outline')['default']
'IconStash:arrowReplyDuotone': typeof import('~icons/stash/arrow-reply-duotone')['default']
NavBar: typeof import('./src/ui/NavBar.vue')['default']

1
src/renderer/src/App.vue

@ -1,5 +1,4 @@
<script setup lang="ts">
</script>
<template>

20
src/renderer/src/pages/index/index.vue

@ -1,7 +1,23 @@
<script setup lang="ts"></script>
<script setup lang="ts">
import { useUpdaterStore } from "common/event/Updater/hook"
const UpdaterStore = useUpdaterStore()
</script>
<template>
<div h-full flex flex-col>sad</div>
<div gap="20px" h-full>
<div p="20px" flex flex-wrap items-start gap="20px" justify-start>
<div w="calc((100%-4*20px)/5)" shadow v-for="i in 10" :key="i">
<div p-2 text-lg font-bold>导航站</div>
<div p-2 pt-0 text-sm>这是一个导航站</div>
<div flex gap="10px" px-4 py-2 tex border-t="1px solid #E5E5E5">
<div cursor="pointer" text-sm leading-1 py-2 px-3 border="1px solid #E5E5E5" rounded>查看</div>
<div cursor="pointer" text-sm leading-1 py-2 px-3 border="1px solid #E5E5E5" rounded>访问</div>
</div>
</div>
<button @click="UpdaterStore.checkForUpdates()">更新</button>
</div>
</div>
</template>
<style lang="scss" scoped></style>

52
src/renderer/src/ui/NavBar.vue

@ -4,7 +4,7 @@
h="30px"
leading="29px"
pr="137px"
:style="{ paddingRight: isFullScreen ? '0' : '' }"
:style="{ paddingRight: PlatForm.isFullScreen.value ? '0' : '' }"
select-none
border-b="1px solid #E5E5E5"
bg="#F8F8F8"
@ -20,6 +20,20 @@
</div>
<div float-right h-full flex items-center relative style="-webkit-app-region: no-drag">
<div
v-if="UpdaterStore.isNeedUpdate"
text-sm
px-2
py-1
flex
items-center
hover:bg-gray-2
hover:cursor-pointer
text="hover:hover"
@click="UpdaterStore.checkForUpdates"
>
<icon-grommet-icons:update :class="{ rotate: UpdaterStore.isChecking }"></icon-grommet-icons:update>
</div>
<div
v-if="!isHome"
text-sm
px-2
@ -59,17 +73,17 @@
import { PopupMenu } from "@/bridge/PopupMenu"
import { usePlatForm } from "common/event/PlatForm/hook"
import { LogLevel } from "logger/common"
import { useUpdaterStore } from "common/event/Updater/hook"
const PlatForm = usePlatForm()
const UpdaterStore = useUpdaterStore()
const router = useRouter()
const route = useRoute()
const isFullScreen = ref(false)
const curLogLevel = ref<LogLevel>()
onBeforeMount(async () => {
isFullScreen.value = await PlatForm.isFullScreen()
curLogLevel.value = await PlatForm.logGetLevel()
curLogLevel.value = await PlatForm.power.logGetLevel()
})
const isHome = computed(() => {
@ -97,30 +111,30 @@
{
label: t("browser.navbar.menu.toggleDevTools"),
async click() {
PlatForm.toggleDevTools()
PlatForm.power.toggleDevTools()
},
},
{
label: "重载",
async click() {
PlatForm.reload()
PlatForm.power.reload()
},
},
{
label: "崩溃",
async click() {
PlatForm.crash()
PlatForm.power.crash()
},
},
{
label: curLogLevel.value === LogLevel.TRACE ? "关闭调试模式" : "开启调试模式",
async click() {
if (curLogLevel.value === LogLevel.TRACE) {
await PlatForm.logSetLevel(LogLevel.INFO)
await PlatForm.power.logSetLevel(LogLevel.INFO)
curLogLevel.value = LogLevel.INFO
return
}
await PlatForm.logSetLevel(LogLevel.TRACE)
await PlatForm.power.logSetLevel(LogLevel.TRACE)
curLogLevel.value = LogLevel.TRACE
},
},
@ -134,10 +148,9 @@
async click(e) {
const menu = new PopupMenu([
{
label: isFullScreen.value ? t("browser.navbar.menu.quit-fullscreen") : t("browser.navbar.menu.fullscreen"),
label: PlatForm.isFullScreen.value ? t("browser.navbar.menu.quit-fullscreen") : t("browser.navbar.menu.fullscreen"),
async click() {
await PlatForm.toggleFullScreen()
isFullScreen.value = !isFullScreen.value
PlatForm.toggleFullScreen()
},
},
])
@ -148,7 +161,7 @@
]
const onClickAbout = () => {
PlatForm.showAbout()
PlatForm.power.showAbout()
}
</script>
@ -161,4 +174,17 @@
@apply: text-sm px-2 hover:rounded-md hover:bg-gray-2 hover:cursor-pointer text="hover:hover";
}
}
.rotate {
animation: rotate 1.5s linear infinite forwards running;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>

14
src/types/global.d.ts

@ -1,11 +1,11 @@
type FireFN = (...argu: any[]) => void
type Api<T extends Record<string | symbol, FireFN>> = {
call: (command: string, ...args: any[]) => any
callLong: (command: string, ...args: any[]) => any
callSync: (command: string, ...args: any[]) => any
send: (command: string, ...argu: any[]) => any
sendSync: (command: string, ...argu: any[]) => any
type Api<M extends Record<string, (...argu: any[]) => void>, T extends Record<string, FireFN>, N extends string> = {
call: <S extends keyof M>(command: `${N}${N extends string ? "." : ""}${S}`, ...args: Parameters<M[S]>) => any
callLong: <S extends keyof M>(command: string, ...args: Parameters<T[S]>) => any
callSync: <S extends keyof M>(command: string, ...args: Parameters<M[S]>) => any
send: <S extends keyof M>(command: string, ...argu: Parameters<M[S]>) => any
sendSync: <S extends keyof M>(command: string, ...argu: Parameters<M[S]>) => any
on: <S extends keyof T>(command: S, cb: (event: IpcRendererEventIpcRendererEvent, ...args: Parameters<T[S]>) => void) => () => void
once: <S extends keyof T>(command: S, cb: (event: IpcRendererEvent, ...args: Parameters<T[S]>) => void) => () => void
off: <S extends keyof T>(command: S, cb: (event: IpcRendererEvent, ...args: Parameters<T[S]>) => void) => void
@ -15,7 +15,7 @@ type Api<T extends Record<string | symbol, FireFN>> = {
declare const electron: typeof import("@electron-toolkit/preload").electronAPI
declare const api: Api
declare const getApi: <T>() => Api<T>
declare const getApi: <M, T, N>() => Api<M, T, N>
interface Window {
electron: typeof import("@electron-toolkit/preload").electronAPI

3
temp/dev.yml

@ -0,0 +1,3 @@
provider: "github"
owner: "npmrun"
repo: "wood-desktop"
Loading…
Cancel
Save