提交 4a06282c 作者: test

feat: 新增一些基础配置、优化打包配置等

上级 f3acc5af
......@@ -2,3 +2,5 @@
* The name of the configuration file entered in the production environment
*/
export const GLOB_CONFIG_FILE_NAME = '_app.config.js'
export const OUTPUT_DIR = 'out/renderer'
......@@ -2,6 +2,6 @@
* Get the configuration file variable name
* @param env
*/
export const getConfigFileName = (env: Record<string, any>) => {
export function getConfigFileName(env: Record<string, any>) {
return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, '')
}
/**
* Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
*/
import fs, { writeFileSync } from 'fs-extra'
import colors from 'picocolors'
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant'
import { getEnvConfig, getRootPath } from '../utils'
import { getConfigFileName } from '../getConfigFileName'
import pkg from '../../package.json'
interface CreateConfigParams {
configName: string
config: any
configFileName?: string
}
function createConfig(params: CreateConfigParams) {
const { configName, config, configFileName } = params
try {
const windowConf = `window.${configName}`
// Ensure that the variable will not be modified
let configStr = `${windowConf}=${JSON.stringify(config)};`
configStr += `
Object.freeze(${windowConf});
Object.defineProperty(window, "${configName}", {
configurable: false,
writable: false,
});
`.replace(/\s/g, '')
fs.mkdirp(getRootPath(OUTPUT_DIR))
writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr)
console.log(`${colors.cyan(`✨ [${pkg.name}]`)} - configuration file is build successfully:`)
console.log(`${colors.gray(`${OUTPUT_DIR}/${colors.green(configFileName)}`)}\n`)
} catch (error) {
console.log(colors.red(`configuration file configuration file failed to package:\n${error}`))
}
}
export function runBuildConfig() {
const config = getEnvConfig()
const configFileName = getConfigFileName(config)
createConfig({ config, configName: configFileName, configFileName: GLOB_CONFIG_FILE_NAME })
}
// #!/usr/bin/env node
import colors from 'picocolors'
import pkg from '../../package.json'
import { runBuildConfig } from './buildConf'
export async function runBuild() {
try {
const argvList = process.argv.splice(2)
// Generate configuration file
if (!argvList.includes('disabled-config')) {
runBuildConfig()
}
console.log(`✨ ${colors.cyan(`[${pkg.name}]`)}` + ' - build successfully!')
} catch (error) {
console.log(colors.red(`vite build error:\n${error}`))
process.exit(1)
}
}
runBuild()
......@@ -15,7 +15,6 @@ import { configVisualizerConfig } from './visualizer'
import { configThemePlugin } from './theme'
import { configSvgIconsPlugin } from './svgSprite'
import { configAutoImportPlugin } from './autoImport'
import { configBuildPlugin } from './config'
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
const { VITE_USE_MOCK, VITE_LEGACY, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv
......@@ -65,7 +64,7 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// The following plugins only work in the production environment
if (isBuild) {
// config
vitePlugins.push(configBuildPlugin())
// vitePlugins.push(configBuildPlugin())
if (process.env.RUNTIME !== 'electron') {
// rollup-plugin-gzip
......
appId: com.yiring.basic.electron.app
productName: 'Basic Electron App'
appId: com.yiring.basic.electron.app
asar: true
npmRebuild: false
buildDependenciesFromSource: true
directories:
buildResources: build
output: release/${version}
output: 'release/${version}'
files:
- 'out'
- 'build/electron'
asarUnpack:
- '**/*.{node,dll}'
afterSign: build/electron/notarize.js
win:
executableName: basic-electron-app
icon: build/electron/icon.ico
artifactName: '${productName}_${version}_${arch}.${ext}'
target:
- target: nsis
arch:
- x64
nsis:
artifactName: ${productName}_${version}.${ext}
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always
......@@ -20,25 +24,6 @@ nsis:
perMachine: false
allowToChangeInstallationDirectory: true
deleteAppDataOnUninstall: false
mac:
entitlementsInherit: build/electron/entitlements.mac.plist
extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
dmg:
artifactName: ${name}-${version}.${ext}
linux:
target:
- AppImage
- snap
- deb
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${name}-${version}.${ext}
npmRebuild: false
publish:
provider: generic
url: https://example.com/auto-updates
......@@ -44,6 +44,9 @@ export default defineConfig({
input: {
index: resolve(__dirname, 'index.html'),
},
// 排除 electron + node 相关的依赖
external: ['node:*', 'path', 'fs', 'process', 'electron', 'electron-store'],
},
},
}
......
......@@ -26,9 +26,9 @@
"bootstrap": "pnpm install",
"serve": "npm run dev",
"dev": "vite",
"dev:app": "electron-vite dev",
"dev:app": "electron-vite dev --watch",
"preview:app": "electron-vite preview",
"build:app": "electron-vite build",
"build:app": "electron-vite build && esno ./build/script/postBuild.ts",
"build:win": "npm run build:app && electron-builder --win --config",
"build:mac": "npm run build:app && electron-builder --mac --config",
"build:linux": "npm run build:app && electron-builder --linux --config",
......@@ -86,12 +86,14 @@
"default-passive-events": "^2.0.0",
"echarts": "^5.4.3",
"electron-log": "^4.4.8",
"electron-store": "^8.1.0",
"electron-updater": "^5.3.0",
"intro.js": "^7.0.1",
"js-file-download": "^0.4.12",
"lodash-es": "^4.17.21",
"mockjs": "^1.1.0",
"nanoid": "^4.0.2",
"node-machine-id": "^1.1.12",
"nprogress": "^0.2.0",
"path-to-regexp": "^6.2.1",
"pinia": "^2.1.6",
......
import type { BrowserWindow } from 'electron'
import { app, ipcMain } from 'electron'
import type ElectronStore from 'electron-store'
import type { SettingStore } from '/#/store'
export async function useHandler(win: BrowserWindow, store: ElectronStore<SettingStore>) {
// ==================== ipc handle ===================
// 关闭程序
ipcMain.handle('close', () => {
win.close()
app.quit()
})
// 重新加载
ipcMain.handle('reload', () => {
win.reload()
})
// 打开 F12
ipcMain.handle('debug', () => {
win.webContents.toggleDevTools()
})
// 最大化
ipcMain.handle('max', () => {
if (win.isMaximized()) {
win.restore()
} else {
win.maximize()
}
})
// 最小化
ipcMain.handle('min', () => {
win.minimize()
})
// 置顶
ipcMain.handle('top', (_, value: boolean) => {
win.setAlwaysOnTop(value)
})
// 设置窗口是否总在最前
if (store.get('system.alwaysOnTop')) {
win.setAlwaysOnTop(true)
}
}
......@@ -2,30 +2,37 @@ import path from 'node:path'
import { BrowserWindow, app, shell } from 'electron'
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
import { useStore } from './store'
import { useUpdater } from './update'
import { useHandler } from './handler'
// Remove electron security warnings
// This warning only shows in development mode
// Read more on https://www.electronjs.org/docs/latest/tutorial/security
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'
process.env.DIST_ELECTRON = path.join(__dirname, '..')
process.env.DIST = path.join(process.env.DIST_ELECTRON, '../dist')
process.env.DIST = path.join(process.env.DIST_ELECTRON, '../out/renderer')
process.env.PUBLIC = app.isPackaged ? __dirname : path.join(process.env.DIST_ELECTRON, '../public')
function createWindow(): void {
async function createWindow(): Promise<BrowserWindow> {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 900,
height: 680,
height: 685,
show: false,
autoHideMenuBar: true,
...(process.platform === 'linux'
? {
icon: path.join(__dirname, '../../build/electron/icon.png'),
}
: {}),
// icon: path.join(__dirname, '../../build/electron/icon.ico'),
webPreferences: {
preload: path.join(__dirname, '../preload/index.js'),
sandbox: false,
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
// Consider using contextBridge.exposeInMainWorld
// Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
nodeIntegration: true,
contextIsolation: false,
},
// 无边框
// frame: false,
})
mainWindow.on('ready-to-show', () => {
......@@ -44,12 +51,14 @@ function createWindow(): void {
} else {
mainWindow.loadFile(path.join(process.env.DIST as string, 'index.html'))
}
return mainWindow
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
app.whenReady().then(async () => {
// Set app user model id for windows
electronApp.setAppUserModelId('com.electron')
......@@ -60,13 +69,20 @@ app.whenReady().then(() => {
optimizer.watchWindowShortcuts(window)
})
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
// 创建窗口
const win = await createWindow()
// 创建本地存储
const store = await useStore(win)
// 版本更新
await useUpdater(win, store)
// 业务交互事件监听
await useHandler(win, store)
})
// Quit when all windows are closed, except on macOS. There, it's common
......
import os from 'node:os'
import log from 'electron-log'
log.transports.file.maxSize = 1002430
log.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}]{scope} {text}'
// 需要保存的了路径
log.transports.file.resolvePath = () => `${os.homedir()}\\Documents\\multi-screen-control-client.log`
// 全局的 console.info 写进日志文件
console.info = log.info
export default log
import type { SettingStore } from '/#/store'
import Store from 'electron-store'
import { machineIdSync } from 'node-machine-id'
import type { BrowserWindow } from 'electron'
import { ipcMain } from 'electron'
import { autoUpdater } from 'electron-updater'
export async function useStore(_win: BrowserWindow) {
// 初始化配置
const store = new Store<SettingStore>({
name: 'basic-electron-client-config',
clearInvalidConfig: true,
encryptionKey: 'yiring.com',
defaults: {
system: {
machineId: machineIdSync().toUpperCase(),
// 调试模式
debug: false,
// 是否总在最前
alwaysOnTop: false,
// 检查更新地址
feedURL: '',
},
},
})
// ==================== ipc handle ===================
// 获取配置
ipcMain.handle('store-get', (_, key) => {
return store.get(key)
})
// 设置配置
ipcMain.handle('store-set', (_, key, value) => {
console.log('store-set', key, value)
if (key === 'system') {
const oldValue = store.get(key)
if (oldValue?.feedURL !== value.feedURL) {
try {
// 设置更新地址
autoUpdater.setFeedURL({
provider: 'generic',
url: value.feedURL,
})
} catch (_) {}
}
}
store.set(key, value)
})
// 重置配置
ipcMain.handle('store-reset', (_, keys) => {
store.reset(keys)
})
return store
}
import log from 'electron-log'
import { autoUpdater } from 'electron-updater'
import type { BrowserWindow } from 'electron'
import { ipcMain } from 'electron'
import log from './log'
import type ElectronStore from 'electron-store'
import type { SettingStore } from '/#/store'
autoUpdater.logger = log
log.info('App starting...')
......@@ -14,7 +16,7 @@ type MessageType =
| 'download-progress'
| 'update-downloaded'
export async function useUpdater(win: BrowserWindow) {
export async function useUpdater(win: BrowserWindow, store: ElectronStore<SettingStore>) {
function checkForUpdate() {
autoUpdater.checkForUpdates()
}
......@@ -31,7 +33,7 @@ export async function useUpdater(win: BrowserWindow) {
sendUpdateMessageToWindow({ type: 'update-available', text: `发现新版本 v${info.version}` })
})
autoUpdater.on('update-not-available', () => {
sendUpdateMessageToWindow({ type: 'update-not-available', text: '当前已是最新版本' })
sendUpdateMessageToWindow({ type: 'update-not-available', text: '当前已是最新版本~' })
})
autoUpdater.on('error', (err) => {
sendUpdateMessageToWindow({ type: 'error', text: err.message })
......@@ -57,6 +59,23 @@ export async function useUpdater(win: BrowserWindow) {
checkForUpdate()
})
// 检查更新
checkForUpdate()
try {
const feedURL = store.get('system.feedURL')
if (feedURL) {
// 设置更新地址
autoUpdater.setFeedURL({
provider: 'generic',
url: feedURL as string,
})
// 检查更新
checkForUpdate()
}
} catch (error: any) {
log.info(`[检查更新] 出现异常: ${error}`)
win.webContents.send('app-notify', {
type: 'error',
message: error.message,
})
}
}
......@@ -62,7 +62,7 @@
<!-- 公司 + 版权信息 -->
<div class="about-app-info-wrap">
<div class="app-icon">
<img src="logo.png" />
<img src="/logo.png" />
</div>
<!-- <div class="app-name">{{ $app.name }}</div> -->
<div class="app-description">{{ $app.description }}</div>
......
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
......
......@@ -46,3 +46,37 @@ export interface BeforeMiniState {
menuMode?: MenuModeEnum
menuType?: MenuTypeEnum
}
export type ISystem = {
/**
* 机器 UUID
*/
machineId?: string
/**
* 系统平台
*/
platform?: string
/**
* 本机 IP
*/
ip?: string
/**
* 调试模式
*/
debug?: boolean
/**
* 置顶
*/
alwaysOnTop?: boolean
/**
* 版本更新地址
*/
feedURL?: string
}
export type SettingStore = {
/**
* 系统配置
*/
system?: ISystem
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论