From 12b1f098a1e845d8f330d6dd8a524fff8dd32d20 Mon Sep 17 00:00:00 2001 From: hello_world Date: Sat, 16 May 2026 12:08:44 +0800 Subject: [PATCH 1/5] feat: vue cli to vite --- packages/gui/.env | 4 +- packages/gui/babel.config.cjs | 5 - packages/gui/electron-builder.config.cjs | 37 +- packages/gui/electron.vite.config.cjs | 91 + packages/gui/{public => }/index.html | 9 +- packages/gui/linux-arm64.vue.config.js | 94 - packages/gui/package.json | 34 +- packages/gui/src/background.js | 6 +- packages/gui/src/bridge/api/backend.js | 13 +- packages/gui/src/bridge/front.js | 2 +- packages/gui/src/bridge/tongji/front.js | 3 +- packages/gui/src/bridge/update/backend.js | 6 +- .../src/bridge/update/{front.js => front.jsx} | 0 packages/gui/src/main.js | 2 +- packages/gui/src/preload.js | 3 + packages/gui/src/view/api.js | 3 +- packages/gui/src/view/mixins/plugin.js | 2 +- packages/gui/src/view/pages/help.vue | 2 +- packages/gui/src/view/pages/index.vue | 4 +- packages/gui/src/view/pages/proxy.vue | 2 +- packages/gui/src/view/router/index.js | 18 +- packages/gui/vue.config.cjs | 45 - pnpm-lock.yaml | 6820 ++++------------- 23 files changed, 1637 insertions(+), 5568 deletions(-) delete mode 100644 packages/gui/babel.config.cjs create mode 100644 packages/gui/electron.vite.config.cjs rename packages/gui/{public => }/index.html (64%) delete mode 100644 packages/gui/linux-arm64.vue.config.js rename packages/gui/src/bridge/update/{front.js => front.jsx} (100%) create mode 100644 packages/gui/src/preload.js delete mode 100644 packages/gui/vue.config.cjs diff --git a/packages/gui/.env b/packages/gui/.env index 55c49958bf..5c5a6c11b5 100644 --- a/packages/gui/.env +++ b/packages/gui/.env @@ -1,2 +1,2 @@ -VUE_APP_PUBLISH_URL=http://dev-sidecar.docmirror.cn/update/ -VUE_APP_PUBLISH_PROVIDER=generic +VITE_PUBLISH_URL=http://dev-sidecar.docmirror.cn/update/ +VITE_PUBLISH_PROVIDER=generic diff --git a/packages/gui/babel.config.cjs b/packages/gui/babel.config.cjs deleted file mode 100644 index 0a932f18f5..0000000000 --- a/packages/gui/babel.config.cjs +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - presets: [ - '@vue/babel-preset-jsx', - ], -} diff --git a/packages/gui/electron-builder.config.cjs b/packages/gui/electron-builder.config.cjs index 9a860b31e5..5c475f4488 100644 --- a/packages/gui/electron-builder.config.cjs +++ b/packages/gui/electron-builder.config.cjs @@ -1,5 +1,5 @@ -const publishUrl = process.env.VUE_APP_PUBLISH_URL -const publishProvider = process.env.VUE_APP_PUBLISH_PROVIDER +const publishUrl = process.env.VITE_PUBLISH_URL +const publishProvider = process.env.VITE_PUBLISH_PROVIDER /** @type {import('electron-builder').Configuration} */ module.exports = { @@ -13,27 +13,20 @@ module.exports = { }, files: [ { - from: 'dist', - to: 'dist', - filter: [ - '**/*', - '!win-*/**/*', - '!mac-*/**/*', - '!linux-*/**/*', - '!*.zip', - '!*.dmg', - '!*.blockmap', - '!*.exe', - '!*.AppImage', - '!*.deb', - '!*.rpm', - '!*.tar.gz', - '!*.flatpak', - '!builder-*.yml', - '!builder-*.yaml', - ], + from: 'dist/main', + to: 'dist/main', + filter: ['**/*'], + }, + { + from: 'dist/renderer', + to: 'dist/renderer', + filter: ['**/*'], + }, + { + from: 'dist/preload', + to: 'dist/preload', + filter: ['**/*'], }, - 'src/**/*', 'package.json', 'extra/**/*', ], diff --git a/packages/gui/electron.vite.config.cjs b/packages/gui/electron.vite.config.cjs new file mode 100644 index 0000000000..95cd408da8 --- /dev/null +++ b/packages/gui/electron.vite.config.cjs @@ -0,0 +1,91 @@ +import { defineConfig, externalizeDepsPlugin } from 'electron-vite' +import vue from '@vitejs/plugin-vue' +import vueJsx from '@vitejs/plugin-vue-jsx' +import { resolve } from 'path' + +export default defineConfig({ + main: { + // Electron 主进程配置 + build: { + lib: { + entry: 'src/background.js', + formats: ['es'], + fileName: () => 'index.js', + }, + outDir: 'dist/main', + rollupOptions: { + external: [ + 'electron', + '@starknt/sysproxy', + '@starknt/sysproxy-linux-arm64-gnu', + '@starknt/shutdown-handler-napi', + '@starknt/shutdown-handler-napi-linux-arm64-gnu', + ], + }, + }, + plugins: [externalizeDepsPlugin()], + resolve: { + alias: { + '@': resolve(__dirname, 'src'), + }, + }, + define: { + 'global.GENTLY': true, + }, + }, + preload: { + // 如果没有 preload 脚本,可以禁用 + build: { + lib: { + entry: 'src/preload.js', + formats: ['es'], + fileName: () => 'index.js', + }, + outDir: 'dist/preload', + }, + plugins: [externalizeDepsPlugin()], + }, + renderer: { + // 渲染进程配置 + root: '.', + base: './', + build: { + outDir: 'dist/renderer', + rollupOptions: { + input: { + index: resolve(__dirname, 'index.html'), + }, + output: { + manualChunks(id) { + if (id.includes('node_modules')) { + if (id.includes('vue') || id.includes('vue-router') || id.includes('ant-design-vue')) { + return 'vendor' + } + } + }, + }, + }, + }, + plugins: [ + vue(), + vueJsx(), + ], + resolve: { + alias: { + '@': resolve(__dirname, 'src'), + }, + }, + css: { + preprocessorOptions: { + scss: {}, + }, + }, + define: { + 'global.GENTLY': true, + }, + server: { + port: 8080, + open: false, + }, + }, +}) diff --git a/packages/gui/public/index.html b/packages/gui/index.html similarity index 64% rename from packages/gui/public/index.html rename to packages/gui/index.html index 1b8de0d375..fea60e28fb 100644 --- a/packages/gui/public/index.html +++ b/packages/gui/index.html @@ -2,10 +2,9 @@ - - - <%= htmlWebpackPlugin.options.title %> + + DevSidecar-给开发者的边车辅助工具 @@ -13,9 +12,9 @@
- +
- + diff --git a/packages/gui/linux-arm64.vue.config.js b/packages/gui/linux-arm64.vue.config.js deleted file mode 100644 index 58e4337573..0000000000 --- a/packages/gui/linux-arm64.vue.config.js +++ /dev/null @@ -1,94 +0,0 @@ -const path = require('node:path') -const { defineConfig } = require('@vue/cli-service') -const webpack = require('webpack') - -const publishUrl = process.env.VUE_APP_PUBLISH_URL -const publishProvider = process.env.VUE_APP_PUBLISH_PROVIDER -console.log('Publish url:', publishUrl) - -module.exports = defineConfig({ - pages: { - index: { - entry: 'src/main.js', - title: 'DevSidecar-给开发者的边车辅助工具', - }, - }, - lintOnSave: false, - configureWebpack: { - plugins: [ - new webpack.DefinePlugin({ 'global.GENTLY': true }), - ], - module: { - rules: [ - { - test: /\.json5$/i, - loader: 'json5-loader', - options: { - esModule: false, - }, - type: 'javascript/auto', - }, - ], - }, - }, - pluginOptions: { - electronBuilder: { - mainProcessFile: './src/background.js', - // Ref: https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/1891 - customFileProtocol: './', - externals: [ - '@starknt/sysproxy', - '@starknt/sysproxy-linux-arm64-gnu', - '@starknt/shutdown-handler-napi', - '@starknt/shutdown-handler-napi-linux-arm64-gnu', - ], - nodeIntegration: true, - // Provide an array of files that, when changed, will recompile the main process and restart Electron - // Your main process file will be added by default - mainProcessWatch: ['src/bridge', 'src/*.js', 'node_modules/dev-sidecar/src'], - builderOptions: { - afterPack: './pkg/after-pack.js', - afterAllArtifactBuild: './pkg/after-all-artifact-build.js', - // artifactBuildCompleted: './pkg/artifact-build-completed.js', - // builderOptions: { - // publish: ['github']// 此处写入github 就好,不用添加其他内容 - // }, - extraResources: [ - { - from: 'extra', - to: 'extra', - }, - ], - appId: 'cn.docmirror.DevSidecar', - productName: 'dev-sidecar', - // eslint-disable-next-line no-template-curly-in-string - artifactName: 'DevSidecar-${version}-${arch}.${ext}', - copyright: 'Copyright © 2020-' + new Date().getFullYear() + ' Greper, WangLiang, CuteOmega', - nsis: { - oneClick: false, - perMachine: true, - allowElevation: true, - allowToChangeInstallationDirectory: true, - }, - linux: { - icon: 'build/mac/', - target: [ - { - target: 'flatpak', - arch: ['arm64', 'armv7l'], - }, - ], - category: 'System', - }, - publish: { - provider: publishProvider, - url: publishUrl, - // url: 'http://dev-sidecar.docmirror.cn/update/preview/', - }, - }, - chainWebpackMainProcess (config) { - config.entry('mitmproxy').add(path.join(__dirname, 'src/bridge/mitmproxy.js')) - }, - }, - }, -}) diff --git a/packages/gui/package.json b/packages/gui/package.json index f43b57c40d..233724012d 100644 --- a/packages/gui/package.json +++ b/packages/gui/package.json @@ -3,7 +3,7 @@ "version": "2.1.0", "private": false, "type": "module", - "main": "src/background.js", + "main": "dist/main/index.js", "author": { "email": "xiaojunnuo@qq.com", "name": "Greper" @@ -11,11 +11,12 @@ "license": "MPL-2.0", "homepage": "https://github.com/docmirror/dev-sidecar", "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", - "lint": "vue-cli-service lint", - "electron": "concurrently -k \"vue-cli-service serve --port 8080\" \"wait-on http-get://localhost:8080 && cross-env WEBPACK_DEV_SERVER_URL=http://localhost:8080 electron .\"", - "electron:build": "npm run build && electron-builder --config electron-builder.config.cjs", + "dev": "electron-vite dev", + "build": "electron-vite build", + "preview": "electron-vite preview", + "lint": "eslint .", + "electron": "electron-vite dev", + "electron:build": "electron-vite build && electron-builder --config electron-builder.config.cjs", "postinstall": "electron-builder install-app-deps", "postuninstall": "electron-builder install-app-deps", "electron:icons": "electron-icon-builder --input=./public/logo/win.png --output=build --flatten", @@ -31,7 +32,6 @@ "adm-zip": "^0.5.17", "ant-design-vue": "^4.2.6", "archiver": "^7.0.1", - "core-js": "^3.49.0", "electron-baidu-tongji": "^1.0.5", "electron-updater": "^6.8.3", "json5": "^2.2.3", @@ -39,27 +39,23 @@ "minimist": "^1.2.8", "request-progress": "^3.0.0", "sass": "^1.99.0", - "sass-loader": "^16.0.7", "tiny-emitter": "^2.1.0", "vue": "^3.5.33", "vue-router": "^4.6.4", "vue3-json-editor": "^1.1.5" }, "devDependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-syntax-jsx": "^7.28.6", - "@vue/babel-helper-vue-jsx-merge-props": "^1.4.0", - "@vue/babel-preset-jsx": "^1.4.0", - "@vue/cli-plugin-babel": "^5.0.9", - "@vue/cli-service": "^5.0.9", - "concurrently": "^8.2.2", - "cross-env": "^7.0.3", + "@vitejs/plugin-vue": "^6.0.7", + "@vitejs/plugin-vue-jsx": "^5.1.5", "electron": "^41.3.0", "electron-builder": "^26.8.1", "electron-icon-builder": "^2.0.1", - "json5-loader": "^4.0.1", - "path-browserify": "^1.0.1", - "wait-on": "^7.2.0" + "electron-vite": "^5.0.0", + "esbuild": "^0.28.0", + "rollup": "^4.60.4", + "vite": "^8.0.13", + "vite-plugin-electron": "^0.29.1", + "vite-plugin-electron-renderer": "^0.14.7" }, "browserslist": [ "> 1%", diff --git a/packages/gui/src/background.js b/packages/gui/src/background.js index 5844616ced..680228c80d 100644 --- a/packages/gui/src/background.js +++ b/packages/gui/src/background.js @@ -234,15 +234,15 @@ function createWindow (startHideWindow, autoQuitIfError = true) { _powerMonitor.setupMainWindow(win) } - if (process.env.WEBPACK_DEV_SERVER_URL) { + if (process.env.VITE_DEV_SERVER_URL) { // Load the url of the dev server if in development mode - win.loadURL(process.env.WEBPACK_DEV_SERVER_URL) + win.loadURL(process.env.VITE_DEV_SERVER_URL) if (!process.env.IS_TEST) { setTimeout(openDevTools, 2000) } } else { // Load the index.html when not in development - win.loadFile(path.join(app.getAppPath(), 'dist', 'index.html')) + win.loadFile(path.join(app.getAppPath(), 'dist', 'renderer', 'index.html')) } if (startHideWindow) { diff --git a/packages/gui/src/bridge/api/backend.js b/packages/gui/src/bridge/api/backend.js index 2441882d90..9264530708 100644 --- a/packages/gui/src/bridge/api/backend.js +++ b/packages/gui/src/bridge/api/backend.js @@ -5,9 +5,9 @@ import DevSidecar from '@docmirror/dev-sidecar' import { app, ipcMain } from 'electron' import lodash from 'lodash' import jsonApi from '@docmirror/mitmproxy/src/json.js' -import { createRequire } from 'node:module' -const require = createRequire(import.meta.url) -const pk = require('../../../package.json') +import { readFileSync } from 'node:fs' +import { join } from 'node:path' +const pk = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf-8')) import coreDefaultConfig from '@docmirror/dev-sidecar/src/config/index.js' import configLoader from '@docmirror/dev-sidecar/src/config/local-config-loader.js' import log from '../../utils/util.log.gui.js' @@ -16,7 +16,12 @@ import dateUtil from '@docmirror/dev-sidecar/src/utils/util.date.js' const { configFromFiles } = coreDefaultConfig const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const mitmproxyPath = path.join(__dirname, '../mitmproxy.js') +// 根据环境判断 mitmproxy.js 的路径 +// electron-vite 开发模式下,主进程构建输出在 dist/main/,所以 mitmproxy.js 也在同一目录 +const isDev = process.env.NODE_ENV !== 'production' +const mitmproxyPath = isDev + ? path.join(process.cwd(), 'dist', 'main', 'mitmproxy.js') + : path.join(app.getAppPath(), 'dist', 'main', 'mitmproxy.js') process.env.DS_EXTRA_PATH = path.join(app.getAppPath(), 'extra') let currentWin diff --git a/packages/gui/src/bridge/front.js b/packages/gui/src/bridge/front.js index c52a2e72c7..b03fcef58d 100644 --- a/packages/gui/src/bridge/front.js +++ b/packages/gui/src/bridge/front.js @@ -4,7 +4,7 @@ import error from './error/front' import fileSelector from './file-selector/front' import onClose from './on-close/front' import tongji from './tongji/front' -import update from './update/front' +import update from './update/front.jsx' const modules = { // api, // 核心接口模块 diff --git a/packages/gui/src/bridge/tongji/front.js b/packages/gui/src/bridge/tongji/front.js index 4d54f3bc8b..fb2746895c 100644 --- a/packages/gui/src/bridge/tongji/front.js +++ b/packages/gui/src/bridge/tongji/front.js @@ -52,11 +52,12 @@ function ebtRenderer (ipcRenderer, siteId, router) { ipcRenderer.send('electron-baidu-tongji-message', siteId) } +import { ipcRenderer } from 'electron' + export default { install (app, api, router) { const BAIDU_SITE_ID = 'f2d170ce560aef0005b689f28697f852' // 百度统计 - const { ipcRenderer } = require('electron') ebtRenderer(ipcRenderer, BAIDU_SITE_ID, router) }, ebtRenderer, diff --git a/packages/gui/src/bridge/update/backend.js b/packages/gui/src/bridge/update/backend.js index cb39cb24fd..88d3115dac 100644 --- a/packages/gui/src/bridge/update/backend.js +++ b/packages/gui/src/bridge/update/backend.js @@ -8,9 +8,9 @@ import electronUpdater from 'electron-updater' const { autoUpdater } = electronUpdater import request from 'request' import progress from 'request-progress' -import { createRequire } from 'node:module' -const require = createRequire(import.meta.url) -const pkg = require('../../../package.json') +import { readFileSync } from 'node:fs' +import { join } from 'node:path' +const pkg = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf-8')) import appPathUtil from '../../utils/util.apppath.js' import log from '../../utils/util.log.gui.js' import { isNewVersion } from '@docmirror/dev-sidecar/src/utils/util.version.js' diff --git a/packages/gui/src/bridge/update/front.js b/packages/gui/src/bridge/update/front.jsx similarity index 100% rename from packages/gui/src/bridge/update/front.js rename to packages/gui/src/bridge/update/front.jsx diff --git a/packages/gui/src/main.js b/packages/gui/src/main.js index 0f067b140e..fb844feeb7 100644 --- a/packages/gui/src/main.js +++ b/packages/gui/src/main.js @@ -4,7 +4,7 @@ import { createRouter, createWebHashHistory } from 'vue-router'; import { ipcRenderer } from 'electron' import view from './view' import App from './view/App.vue' -import DsContainer from './view/components/container' +import DsContainer from './view/components/container.vue' import routes from './view/router' import 'ant-design-vue/dist/reset.css' import './view/style/index.scss' diff --git a/packages/gui/src/preload.js b/packages/gui/src/preload.js new file mode 100644 index 0000000000..7222109bfa --- /dev/null +++ b/packages/gui/src/preload.js @@ -0,0 +1,3 @@ +// Preload script +// 当前项目未使用 preload,但 electron-vite 需要此文件存在 +// 如需启用 contextIsolation,可在此暴露安全的 API diff --git a/packages/gui/src/view/api.js b/packages/gui/src/view/api.js index 66e59c7a6b..0065f4db9b 100644 --- a/packages/gui/src/view/api.js +++ b/packages/gui/src/view/api.js @@ -1,6 +1,5 @@ import { ipcRenderer, shell } from 'electron' import lodash from 'lodash' -import path from 'path' let inited = false let apiObj = null @@ -39,7 +38,7 @@ export function apiInit (app) { await shell.openExternal(href) }, openPath (file) { - shell.openPath(path.resolve(file)) + shell.openPath(file) }, }, } diff --git a/packages/gui/src/view/mixins/plugin.js b/packages/gui/src/view/mixins/plugin.js index bff7b52f96..3add575fc4 100644 --- a/packages/gui/src/view/mixins/plugin.js +++ b/packages/gui/src/view/mixins/plugin.js @@ -1,5 +1,5 @@ import lodash from 'lodash' -import DsContainer from '../components/container' +import DsContainer from '../components/container.vue' export default { components: { diff --git a/packages/gui/src/view/pages/help.vue b/packages/gui/src/view/pages/help.vue index 64994feaec..982b831e49 100644 --- a/packages/gui/src/view/pages/help.vue +++ b/packages/gui/src/view/pages/help.vue @@ -3,7 +3,7 @@ import { defineComponent } from 'vue'; import { ProfileOutlined } from '@ant-design/icons-vue' import Plugin from '../mixins/plugin' -import TreeNode from '../components/tree-node' +import TreeNode from '../components/tree-node.vue' export default defineComponent({ name: 'Help', diff --git a/packages/gui/src/view/pages/index.vue b/packages/gui/src/view/pages/index.vue index 9c93a37b43..757238fd99 100644 --- a/packages/gui/src/view/pages/index.vue +++ b/packages/gui/src/view/pages/index.vue @@ -1,8 +1,8 @@ + diff --git a/packages/gui/src/view/App.vue b/packages/gui/src/renderer/src/App.vue similarity index 98% rename from packages/gui/src/view/App.vue rename to packages/gui/src/renderer/src/App.vue index de21789538..897eb63ed0 100644 --- a/packages/gui/src/view/App.vue +++ b/packages/gui/src/renderer/src/App.vue @@ -2,11 +2,13 @@ import { h } from 'vue'; import * as Icons from '@ant-design/icons-vue'; -import { ipcRenderer } from 'electron' -import createMenus from '@/view/router/menu' +import createMenus from '@/router/menu' import zhCN from 'ant-design-vue/es/locale/zh_CN' import { colorTheme } from './composables/theme' +// 从 preload 暴露的 electron API 获取 ipcRenderer +const { ipcRenderer } = window.electron + export default { name: 'App', @@ -363,7 +365,7 @@ body { padding: 5px; border-bottom: #eee solid 1px; height: 60px; - background-image: url('../../public/logo/logo-lang.svg'); + background-image: url('/logo/logo-lang.svg'); background-size: auto 50px; background-repeat: no-repeat; background-position: 5px center; diff --git a/packages/gui/src/view/api.js b/packages/gui/src/renderer/src/api.js similarity index 93% rename from packages/gui/src/view/api.js rename to packages/gui/src/renderer/src/api.js index 0065f4db9b..9dc6680106 100644 --- a/packages/gui/src/view/api.js +++ b/packages/gui/src/renderer/src/api.js @@ -1,6 +1,8 @@ -import { ipcRenderer, shell } from 'electron' import lodash from 'lodash' +// 从 preload 暴露的 electron API 获取 ipcRenderer 和 shell +const { ipcRenderer, shell } = window.electron + let inited = false let apiObj = null export function apiInit (app) { diff --git a/packages/gui/src/renderer/src/bridge/auto-start/front.js b/packages/gui/src/renderer/src/bridge/auto-start/front.js new file mode 100644 index 0000000000..1e18cdb5d4 --- /dev/null +++ b/packages/gui/src/renderer/src/bridge/auto-start/front.js @@ -0,0 +1,18 @@ +function install (app, api) { + api.ipc.on('auto-start', (event, message) => { + if (message.value === true) { + app.config.globalProperties.$message.info('已添加开机自启') + } else { + app.config.globalProperties.$message.info('已取消开机自启') + } + }) + api.autoStart = { + async enabled (value) { + api.ipc.send('auto-start', { key: 'enabled', value }) + }, + } +} + +export default { + install, +} diff --git a/packages/gui/src/renderer/src/bridge/error/front.js b/packages/gui/src/renderer/src/bridge/error/front.js new file mode 100644 index 0000000000..68074e586c --- /dev/null +++ b/packages/gui/src/renderer/src/bridge/error/front.js @@ -0,0 +1,47 @@ +let latestConfirmTime = null + +function install (app, api) { + api.ipc.on('error.core', (event, message) => { + console.error('view on error', message) + const key = message.key + if (key === 'server') { + handleServerStartError(message, message.error, app, api) + } + }) + api.ipc.on('error', (event, message) => { + console.error('error', event, message) + }) +} + +function handleServerStartError (message, err, app, api) { + if (message.value === 'EADDRINUSE') { + // 避免重复弹窗 + const now = Date.now() + if (latestConfirmTime != null && now - latestConfirmTime < 1000) { + return + } + latestConfirmTime = now + + app.config.globalProperties.$confirm({ + title: '端口被占用,代理服务启动失败', + content: '是否要杀掉占用进程?您也可以点击取消,然后前往加速服务->基本设置中修改代理端口', + onOk () { + api.config.get().then((config) => { + console.log('config:', config) + api.shell.killByPort({ port: config.server.port }).then((ret) => { + app.config.globalProperties.$message.info('杀掉进程成功,请重试开启代理服务') + }) + }) + }, + onCancel () { + console.log('Cancel') + }, + }) + } else { + app.config.globalProperties.$message.error(`加速服务启动失败:${message.message}`) + } +} + +export default { + install, +} diff --git a/packages/gui/src/renderer/src/bridge/file-selector/front.js b/packages/gui/src/renderer/src/bridge/file-selector/front.js new file mode 100644 index 0000000000..5ceff2cdb1 --- /dev/null +++ b/packages/gui/src/renderer/src/bridge/file-selector/front.js @@ -0,0 +1,63 @@ +function install (app, api) { + api.fileSelector = { + + /** + * 打开文件选择框 + * + * 支持传参方式: + * 1. open(String defaultPath) + * 2. open(String defaultPath, String properties) + * 3. open(null, String properties) + * 4. open(String defaultPath, Object options) + * 5. open(Object options) + * + * @param value + * @param {Electron.OpenDialogOptions} options + * @returns {Promise} promise + */ + open (value = null, options = null) { + if (options == null && value && typeof value !== 'string') { + options = { ...value } + value = null + } else { + if (typeof options === 'string') { + if (options === 'dir') { + options = 'openDirectory' + } else if (options === 'file') { + options = 'openFile' + } + + options = { properties: [options] } // options 为字符串时,视为 properties 属性的值 + } else { + options = options || {} + } + } + + // 如果没有 defaultPath,则使用 value 作为 defaultPath + if (!options.defaultPath && value && typeof value === 'string') { + options.defaultPath = value + } + + return new Promise((resolve, reject) => { + api.ipc.send('file-selector', { key: 'open', options }) + api.ipc.on('file-selector', (event, message) => { + console.log('selector', message) + if (message.key === 'selected') { + resolve(message.value) + } else if (message.key === 'canceled') { + resolve('') // 没有选择文件 + } else if (message.key === 'error') { + reject(message.error) + } else { + reject(new Error('未知的响应')) + } + api.ipc.on('file-selector', () => {}) + }) + }) + }, + } +} + +export default { + install, +} diff --git a/packages/gui/src/renderer/src/bridge/front.js b/packages/gui/src/renderer/src/bridge/front.js new file mode 100644 index 0000000000..b03fcef58d --- /dev/null +++ b/packages/gui/src/renderer/src/bridge/front.js @@ -0,0 +1,25 @@ +// import api from './api/front' +import autoStart from './auto-start/front' +import error from './error/front' +import fileSelector from './file-selector/front' +import onClose from './on-close/front' +import tongji from './tongji/front' +import update from './update/front.jsx' + +const modules = { + // api, // 核心接口模块 + error, + fileSelector, // 文件选择模块 + tongji, // 统计模块 + update, // 自动更新 + autoStart, + onClose, +} +export default { + install (app, api, router) { + for (const module in modules) { + modules[module].install(app, api, router) + } + }, + ...modules, +} diff --git a/packages/gui/src/renderer/src/bridge/on-close/front.js b/packages/gui/src/renderer/src/bridge/on-close/front.js new file mode 100644 index 0000000000..d710d28924 --- /dev/null +++ b/packages/gui/src/renderer/src/bridge/on-close/front.js @@ -0,0 +1,66 @@ +import { h, resolveComponent } from 'vue' + +let closeType = 2 +let doSave = false + +function install (app, api) { + api.ipc.on('close.showTip', (event, message) => { + console.info('ipc channel: "close.showTip", event:', event, ', message:', message) + function onRadioChange (e) { + closeType = parseInt(e.target.value) + } + function onCheckChange (e) { + doSave = e.target.checked + } + + const shortcut = message.showHideShortcut || '无' + + const ARadioGroup = resolveComponent('a-radio-group') + const ARadio = resolveComponent('a-radio') + const ACheckbox = resolveComponent('a-checkbox') + + // 使用 h 函数创建 VNode + const content = h('div', {}, [ + h('div', { style: { marginTop: '10px' } }, [ + h(ARadioGroup, { + value: closeType, + 'onUpdate:value': (val) => { closeType = val }, + onChange: onRadioChange, + }, [ + h(ARadio, { value: 1 }, '直接关闭'), + h(ARadio, { value: 2 }, '最小化到系统托盘'), + ]), + ]), + h('div', { style: { marginTop: '10px' } }, [ + h(ACheckbox, { + checked: doSave, + 'onUpdate:checked': (val) => { doSave = val }, + onChange: onCheckChange, + }, '记住本次选择,不再提示'), + ]), + h('div', { style: { marginTop: '20px' } }, [ + '提示:打开窗口的快捷键为 ', + h('code', {}, shortcut), + ]), + ]) + + app.config.globalProperties.$confirm({ + title: '关闭策略', + content, + async onOk () { + console.log('OK. closeType=', closeType, ', doSave:', doSave) + if (doSave) { + await api.config.update({ app: { closeStrategy: closeType } }) + } + api.ipc.send('close', { key: 'selected', value: closeType }) + }, + onCancel () { + console.log('Cancel. closeType=', closeType) + }, + }) + }) +} + +export default { + install, +} diff --git a/packages/gui/src/renderer/src/bridge/tongji/front.js b/packages/gui/src/renderer/src/bridge/tongji/front.js new file mode 100644 index 0000000000..f46f033a27 --- /dev/null +++ b/packages/gui/src/renderer/src/bridge/tongji/front.js @@ -0,0 +1,65 @@ +/** + * second step + * @param {*} ipcRenderer + * @param {*} siteId + * @param {*} router + */ +function ebtRenderer (ipcRenderer, siteId, router) { + /* istanbul ignore else */ + if (!(ipcRenderer && ipcRenderer.on && ipcRenderer.send)) { + throw new TypeError('require ipcRenderer') + } + + /* istanbul ignore else */ + if (!(siteId && typeof siteId === 'string')) { + throw new TypeError('require siteId') + } + + // step 4 + ipcRenderer.on('electron-baidu-tongji-reply', (_, { text, isDevelopment }) => { + console.log('electron-baidu-tongji-reply') + /* istanbul ignore else */ + if (isDevelopment) { + document.body.classList.add('electron-baidu-tongji_dev') + } + + window._hmt = window._hmt || [] + + const hm = document.createElement('script') + hm.text = text + + const head = document.getElementsByTagName('head')[0] + head.appendChild(hm) + + // Vue单页应用时,监听router的每次变化 + // 把虚拟的url地址赋给百度统计的API接口 + + /* istanbul ignore else */ + if (router && router.beforeEach) { + router.beforeEach((to, _, next) => { + /* istanbul ignore else */ + if (to.path) { + window._hmt.push(['_trackPageview', `/#${to.fullPath}`]) + console.log('baidu trace', to.fullPath) + } + + next() + }) + } + }) + + // step 1 + ipcRenderer.send('electron-baidu-tongji-message', siteId) +} + +// 从 preload 暴露的 electron API 获取 ipcRenderer +const { ipcRenderer } = window.electron + +export default { + install (app, api, router) { + const BAIDU_SITE_ID = 'f2d170ce560aef0005b689f28697f852' + // 百度统计 + ebtRenderer(ipcRenderer, BAIDU_SITE_ID, router) + }, + ebtRenderer, +} diff --git a/packages/gui/src/renderer/src/bridge/update/front.jsx b/packages/gui/src/renderer/src/bridge/update/front.jsx new file mode 100644 index 0000000000..9e90fe62bd --- /dev/null +++ b/packages/gui/src/renderer/src/bridge/update/front.jsx @@ -0,0 +1,248 @@ +function install (app, api) { + const updateParams = app.config.globalProperties.$global.update = { fromUser: false, autoDownload: false, progress: 0, checking: false, downloading: false, newVersion: false, isFullUpdate: true } + api.ipc.on('update', (event, message) => { + console.log('on message', event, message) + handleUpdateMessage(message, app) + }) + + api.update = { + checkForUpdate (fromUser) { + if (fromUser != null) { + updateParams.fromUser = fromUser + } + updateParams.checking = true + api.ipc.send('update', { key: 'checkForUpdate', fromUser }) + }, + downloadUpdate () { + api.ipc.send('update', { key: 'downloadUpdate' }) + }, + downloadPart (value) { + // 增量更新 + api.ipc.send('update', { key: 'downloadPart', value }) + }, + doUpdateNow () { + api.ipc.send('update', { key: 'doUpdateNow' }) + }, + } + + function handleUpdateMessage (message) { + const type = message.key + if (type === 'available') { + updateParams.checking = false + updateParams.newVersionData = message.value + foundNewVersion(message.value) + } else if (type === 'notAvailable') { + updateParams.checking = false + noNewVersion() + } else if (type === 'downloaded') { + // 更新包已下载完成,让用户确认是否更新 + updateParams.downloading = false + console.log('updateParams', updateParams) + newUpdateIsReady(message.value) + } else if (type === 'progress') { + progressUpdate(message.value) + } else if (type === 'error') { + updateParams.checking = false + updateParams.downloading = false + if (message.action === 'checkForUpdate' && updateParams.newVersionData) { + // 如果检查更新报错了,但刚才成功拿到过一次数据,就拿之前的数据 + foundNewVersion(updateParams.newVersionData) + } else { + if (updateParams.fromUser === false && message.action === 'checkForUpdate') { + return // 不是手动检查更新,不提示错误信息,避免打扰 + } + const error = message.error + app.config.globalProperties.$message.error((error == null ? '未知错误' : (error.stack || error).toString())) + } + } + } + + function noNewVersion () { + updateParams.newVersion = false + if (updateParams.fromUser) { + app.config.globalProperties.$message.info('当前已经是最新版本') + } + } + + function progressUpdate (value) { + updateParams.progress = value + } + + function openGithubUrl () { + api.ipc.openExternal('https://github.com/docmirror/dev-sidecar/releases') + } + + function goManualUpdate (value) { + updateParams.newVersion = false + app.config.globalProperties.$confirm({ + title: '暂不提供自动升级', + cancelText: '取消', + okText: '打开链接', + width: 420, + content: (h) => { + return ( +
+
+ 请前往 + github项目release页面 + 下载新版本手动安装 +
+
https://github.com/docmirror/dev-sidecar/releases
+
+ ) + }, + onOk () { + openGithubUrl() + }, + }) + } + + // /** + // * 是否小版本升级 + // * @param value + // */ + // async function isSupportPartUpdate (value) { + // const info = await api.info.get() + // console.log('升级版本:', value.version) + // console.log('增量更新最小版本:', value.partMiniVersion) + // console.log('当前版本:', info.version) + // if (!value.partPackage) { + // return false + // } + // return !!(value.partMiniVersion && value.partMiniVersion < info.version) + // } + + async function downloadNewVersion (value) { + // 暂时取消自动更新功能 + goManualUpdate(value) + + // const platform = await api.shell.getSystemPlatform() + // console.log(`download new version: ${JSON.stringify(value)}, platform: ${platform}`) + // if (platform === 'linux') { + // goManualUpdate(value) + // return + // } + // const partUpdate = await isSupportPartUpdate(value) + // if (partUpdate) { + // // 有增量更新 + // api.update.downloadPart(value) + // } else { + // if (platform === 'mac') { + // goManualUpdate(value) + // return + // } + // updateParams.downloading = true + // api.update.downloadUpdate() + // } + } + function foundNewVersion (value) { + updateParams.newVersion = true + + if (updateParams.autoDownload !== false) { + app.config.globalProperties.$message.info('发现新版本,正在下载中...') + + downloadNewVersion(value) + return + } + console.log(value) + app.config.globalProperties.$confirm({ + title: `发现新版本:v${value.version}`, + cancelText: '暂不升级', + okText: '升级', + width: 700, + content: (h) => { + if (value.releaseNotes) { + const notes = [] + if (typeof value.releaseNotes === 'string') { + const releaseNotes = value.releaseNotes.replace(/\r\n/g, '\n') + return ( +
+
+ 发布公告: + https://github.com/docmirror/dev-sidecar/releases +
+
+
+                  {releaseNotes}
+                
+
+ ) + } else { + for (const note of value.releaseNotes) { + notes.push(
  • {note}
  • ) + } + return ( +
    +
    + 发布公告: + https://github.com/docmirror/dev-sidecar/releases +
    +
    更新内容:
    +
      {notes}
    +
    + ) + } + } + }, + onOk () { + console.log('OK') + downloadNewVersion(value) + }, + onCancel () { + console.log('Cancel') + }, + }) + } + + function newUpdateIsReady (value) { + updateParams.downloading = false + console.log(value) + app.config.globalProperties.$confirm({ + title: `新版本(v${value.version})已准备好,是否立即升级?`, + cancelText: '暂不升级', + okText: '立即升级', + width: 700, + content: (h) => { + if (value.releaseNotes) { + const notes = [] + if (typeof value.releaseNotes === 'string') { + const releaseNotes = value.releaseNotes.replace(/\r\n/g, '\n') + return ( +
    +
    + 发布公告: + https://github.com/docmirror/dev-sidecar/releases +
    +
    +
    +                  {releaseNotes}
    +                
    +
    + ) + } else { + for (const note of value.releaseNotes) { + notes.push(
  • {note}
  • ) + } + return ( +
    +
    + 发布公告: + https://github.com/docmirror/dev-sidecar/releases +
    +
    更新内容:
    +
      {notes}
    +
    + ) + } + } + }, + onOk () { + api.update.doUpdateNow() + }, + }) + } +} + +export default { + install, +} diff --git a/packages/gui/src/view/components/container.vue b/packages/gui/src/renderer/src/components/container.vue similarity index 100% rename from packages/gui/src/view/components/container.vue rename to packages/gui/src/renderer/src/components/container.vue diff --git a/packages/gui/src/view/components/setup-ca.vue b/packages/gui/src/renderer/src/components/setup-ca.vue similarity index 100% rename from packages/gui/src/view/components/setup-ca.vue rename to packages/gui/src/renderer/src/components/setup-ca.vue diff --git a/packages/gui/src/view/components/tree-node.vue b/packages/gui/src/renderer/src/components/tree-node.vue similarity index 100% rename from packages/gui/src/view/components/tree-node.vue rename to packages/gui/src/renderer/src/components/tree-node.vue diff --git a/packages/gui/src/view/composables/theme.js b/packages/gui/src/renderer/src/composables/theme.js similarity index 100% rename from packages/gui/src/view/composables/theme.js rename to packages/gui/src/renderer/src/composables/theme.js diff --git a/packages/gui/src/view/index.js b/packages/gui/src/renderer/src/index.js similarity index 92% rename from packages/gui/src/view/index.js rename to packages/gui/src/renderer/src/index.js index 0d498f02eb..4ee7e89be8 100644 --- a/packages/gui/src/view/index.js +++ b/packages/gui/src/renderer/src/index.js @@ -1,4 +1,4 @@ -import modules from '../bridge/front' +import modules from './bridge/front' import { apiInit, useApi } from './api' import status from './status' diff --git a/packages/gui/src/main.js b/packages/gui/src/renderer/src/main.js similarity index 81% rename from packages/gui/src/main.js rename to packages/gui/src/renderer/src/main.js index fb844feeb7..1c77babcc4 100644 --- a/packages/gui/src/main.js +++ b/packages/gui/src/renderer/src/main.js @@ -1,14 +1,16 @@ import antd from 'ant-design-vue' import { createApp } from 'vue'; import { createRouter, createWebHashHistory } from 'vue-router'; -import { ipcRenderer } from 'electron' -import view from './view' -import App from './view/App.vue' -import DsContainer from './view/components/container.vue' -import routes from './view/router' +import view from './' +import App from './App.vue' +import DsContainer from './components/container.vue' +import routes from './router' import 'ant-design-vue/dist/reset.css' -import './view/style/index.scss' -import './view/style/theme/dark.scss' // 暗色主题 +import './style/index.scss' +import './style/theme/dark.scss' // 暗色主题 + +// 从 preload 暴露的 electron API 获取 ipcRenderer +const { ipcRenderer } = window.electron try { window.onerror = (message, source, lineno, colno, error) => { @@ -29,11 +31,11 @@ try { routes, // (缩写) 相当于 routes: routes }) const app = createApp(App) - + app.use(antd) app.use(router) app.component('DsContainer', DsContainer) - + view.initApi(app).then(async (api) => { // 初始化status try { diff --git a/packages/gui/src/view/mixins/plugin.js b/packages/gui/src/renderer/src/mixins/plugin.js similarity index 100% rename from packages/gui/src/view/mixins/plugin.js rename to packages/gui/src/renderer/src/mixins/plugin.js diff --git a/packages/gui/src/view/pages/help.vue b/packages/gui/src/renderer/src/pages/help.vue similarity index 100% rename from packages/gui/src/view/pages/help.vue rename to packages/gui/src/renderer/src/pages/help.vue diff --git a/packages/gui/src/view/pages/index.vue b/packages/gui/src/renderer/src/pages/index.vue similarity index 100% rename from packages/gui/src/view/pages/index.vue rename to packages/gui/src/renderer/src/pages/index.vue diff --git a/packages/gui/src/view/pages/plugin/free-eye.vue b/packages/gui/src/renderer/src/pages/plugin/free-eye.vue similarity index 100% rename from packages/gui/src/view/pages/plugin/free-eye.vue rename to packages/gui/src/renderer/src/pages/plugin/free-eye.vue diff --git a/packages/gui/src/view/pages/plugin/git.vue b/packages/gui/src/renderer/src/pages/plugin/git.vue similarity index 100% rename from packages/gui/src/view/pages/plugin/git.vue rename to packages/gui/src/renderer/src/pages/plugin/git.vue diff --git a/packages/gui/src/view/pages/plugin/node.vue b/packages/gui/src/renderer/src/pages/plugin/node.vue similarity index 100% rename from packages/gui/src/view/pages/plugin/node.vue rename to packages/gui/src/renderer/src/pages/plugin/node.vue diff --git a/packages/gui/src/view/pages/plugin/overwall.vue b/packages/gui/src/renderer/src/pages/plugin/overwall.vue similarity index 100% rename from packages/gui/src/view/pages/plugin/overwall.vue rename to packages/gui/src/renderer/src/pages/plugin/overwall.vue diff --git a/packages/gui/src/view/pages/plugin/pip.vue b/packages/gui/src/renderer/src/pages/plugin/pip.vue similarity index 100% rename from packages/gui/src/view/pages/plugin/pip.vue rename to packages/gui/src/renderer/src/pages/plugin/pip.vue diff --git a/packages/gui/src/view/pages/proxy.vue b/packages/gui/src/renderer/src/pages/proxy.vue similarity index 100% rename from packages/gui/src/view/pages/proxy.vue rename to packages/gui/src/renderer/src/pages/proxy.vue diff --git a/packages/gui/src/view/pages/server.vue b/packages/gui/src/renderer/src/pages/server.vue similarity index 100% rename from packages/gui/src/view/pages/server.vue rename to packages/gui/src/renderer/src/pages/server.vue diff --git a/packages/gui/src/view/pages/setting.vue b/packages/gui/src/renderer/src/pages/setting.vue similarity index 99% rename from packages/gui/src/view/pages/setting.vue rename to packages/gui/src/renderer/src/pages/setting.vue index 240df00cb7..86e8dcb21f 100644 --- a/packages/gui/src/view/pages/setting.vue +++ b/packages/gui/src/renderer/src/pages/setting.vue @@ -1,6 +1,8 @@