Electron

1、安装 (过程有点慢,耐心等待;建议替换yarn源或翻墙)

yarn add electron --dev
yarn add electron-devtools-installer --dev
yarn add vue-cli-plugin-electron-builder

2、在src目录下新建 background.ts,作为electron入口

import { app, protocol, BrowserWindow, ipcMain } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import path from 'path'
//import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer'
const isDevelopment = process.env.NODE_ENV !== 'production'
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])
let win: any = null
async function createWindow() {
  // Create the browser window.
  win = new BrowserWindow({
    useContentSize: true,
    width: 1220,
    height: 640,
    minWidth: 1220,
    minHeight: 640,
    //transparent: true, //窗口透明  设置后还原窗口win.restore()无效
    //backgroundColor: '#000', //背景颜色
    title: '知乎者也', //标题
    movable: true,
    webPreferences: {
      // Required for Spectron testing
      // enableRemoteModule: !!process.env.IS_TEST,
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      nodeIntegration: process.env
        .ELECTRON_NODE_INTEGRATION as unknown as boolean,
      contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
    }
  })
  if (process.env.NODE_ENV === 'production') {
    //正式
    createProtocol('app')
    win.loadURL('app://./index.html') // Load the index.html when not in development
    //win.webContents.openDevTools()
  } else {
    //开发
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
    if (!process.env.IS_TEST) win.webContents.openDevTools()
  }
  // 当应用所有窗口关闭要做的事情
  win.on('closed', () => {
    win = null
  })
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})
app.on('activate', () => {
  // 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()
})
// 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.on('ready', async () => {
  //外网 加载慢 去掉
  //   if (isDevelopment && !process.env.IS_TEST) {
  //     // Install Vue Devtools
  //     try {
  //       await installExtension(VUEJS3_DEVTOOLS)
  //     } catch (e) {
  //       console.error('Vue Devtools failed to install:', e.toString())
  //     }
  //   }
  if (win === null) createWindow()
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}

3、修改package.json文件,新增入口属性以及运行命令

"scripts": {
        "test:unit": "vue-cli-service test:unit",
        "lint": "vue-cli-service lint",
        "web:serve": "vue-cli-service serve --open",
        "web:build": "vue-cli-service build",
        "electron:build": "vue-cli-service electron:build",
        "electron:serve": "vue-cli-service electron:serve",
        "postinstall": "electron-builder install-app-deps",
        "postuninstall": "electron-builder install-app-deps"
    },

4、根目录下新建vue.config.js 用与保存打包配置

module.exports = {
    runtimeCompiler: true,
    publicPath: './',
    //electron 13 把"build":{}从package.json移除,在vue.config.js里写
    pluginOptions: {
        electronBuilder: {
            nodeIntegration: true,
            builderOptions: {
                productName: 'zhzy', //打包名称
                appId: 'cn.zhzy.app',
                copyright: 'Copyright © 2022',
                publish: [
                    {
                        provider: 'generic',
                        url: ''
                    }
                ],
                nsis: {
                    allowToChangeInstallationDirectory: true,
                    createDesktopShortcut: true,
                    createStartMenuShortcut: true,
                    shortcutName: 'zhzy',
                    perMachine: true,
                    oneClick: false
                },
                dmg: {
                    contents: [
                        {
                            x: 410,
                            y: 150,
                            type: 'link',
                            path: '/Applications'
                        },
                        {
                            x: 130,
                            y: 150,
                            type: 'file'
                        }
                    ]
                },
                //productName 包名称 version 包版本(package.json)  ext后缀
                mac: {
                    icon: 'public/icons/icon.icns',
                    artifactName: '${productName}_setup_${version}.${ext}'
                },
                win: {
                    icon: 'public/icons/icon.ico',
                    artifactName: '${productName}_setup_${version}.${ext}'
                },
                linux: {
                    icon: 'public/icons',
                    artifactName: '${productName}_setup_${version}.${ext}'
                }
            }
        }
    }
}

隐藏菜单栏

在background.ts增加

autoHideMenuBar: true, //是否隐藏菜单栏

无边框模式

1、在background.ts增加

 frame: false, 
无边框   
不使用无边框模式 
需要屏蔽掉  
并将router/index.ts 路由中的替换 
 原始边框路由

background.ts增加

//登录窗口最小化
ipcMain.on('app-min', function () {
  win.minimize()
})
//登录窗口最大化、还原窗口
ipcMain.on('app-max', function () {
  if (win.isMaximized()) {
    win.restore()
  } else {
    win.maximize()
  }
})
ipcMain.on('app-close', function () {
  //win.close() //在设置无边框后失效
  win = null //主窗口设置为null 防止内存溢出
  app.exit() //直接退出应用程序
})

在src目录下新建proload.ts

import { ipcRenderer } from 'electron'
(window as any).ipcRenderer = ipcRenderer

在background.ts webPreferences新增

 preload: path.join(__dirname, 'preload.ts'), //预加载

版本升级

yarn add electron-updater

src目录下新建upgrade.ts

import { ipcMain } from 'electron'
import { autoUpdater } from 'electron-updater'
let mainWindow: any = null
export function upgradeHandle(window: any, feedUrl: any) {
  const msg = {
    error: '检查更新出错 ...',
    checking: '正在检查更 ...',
    updateAva: '检测到新版本 ...',
    updateNotAva: '已经是最新版本 ...',
    downloadProgress: '正在下载新版本 ...',
    downloaded: '下载完成,开始更新 ...'
  }
  mainWindow = window
  autoUpdater.autoDownload =
    process.env.VUE_APP_UPGRADE === 'automatic' ? true : false //true 自动升级 false 手动升级
  //设置更新包的地址
  autoUpdater.setFeedURL(feedUrl)
  //监听升级失败事件
  autoUpdater.on('error', function (message: any) {
    sendUpdateMessage({
      cmd: 'error',
      title: msg.error,
      message: message
    })
  })
  //监听开始检测更新事件
  autoUpdater.on('checking-for-update', function (message: any) {
    sendUpdateMessage({
      cmd: 'checking-for-update',
      title: msg.checking,
      message: message
    })
  })
  //监听发现可用更新事件
  autoUpdater.on('update-available', function (message: any) {
    sendUpdateMessage({
      cmd: 'update-available',
      title: msg.updateAva,
      message: message
    })
  })
  //监听没有可用更新事件
  autoUpdater.on('update-not-available', function (message: any) {
    sendUpdateMessage({
      cmd: 'update-not-available',
      title: msg.updateNotAva,
      message: message
    })
  })
  // 更新下载进度事件
  autoUpdater.on('download-progress', function (message: any) {
    sendUpdateMessage({
      cmd: 'download-progress',
      title: msg.downloadProgress,
      message: message
    })
  })
  //监听下载完成事件
  autoUpdater.on(
    'update-downloaded',
    function (
      event: any,
      releaseNotes: any,
      releaseName: any,
      releaseDate: any,
      updateUrl: any
    ) {
      sendUpdateMessage({
        cmd: 'update-downloaded',
        title: msg.downloaded,
        message: {
          releaseNotes,
          releaseName,
          releaseDate,
          updateUrl
        }
      })
      //退出并安装更新包
      autoUpdater.quitAndInstall()
    }
  )
  //接收渲染进程消息,开始检查更新
  ipcMain.on('checkForUpdate', () => {
    //执行自动更新检查
    autoUpdater.checkForUpdates()
  })
  ipcMain.on('downloadUpdate', () => {
    // 下载
    autoUpdater.downloadUpdate()
  })
}
//给渲染进程发送消息
function sendUpdateMessage(text: any) {
  mainWindow.webContents.send('message', text)
}

background.ts增加

import { upgradeHandle } from '@/upgrade'
if (process.env.NODE_ENV === 'production') {
    //正式
    createProtocol('app')
    win.loadURL('app://./index.html') // Load the index.html when not in development
    if (process.env.VUE_APP_UPLOAD !== '')
      upgradeHandle(win, process.env.VUE_APP_UPLOAD) //检测版本更新
    //win.webContents.openDevTools()

通过.env.development/.env.production设置 开发与正式环境

# 配置生产环境 系统变量 建议配置一些 常用 或者 数据较单一的配置
NODE_ENV=production
# 版本号  应与package.json version 保持统一
VUE_APP_VERSION=0.1.0
# 升级地址
VUE_APP_UPLOAD= http://192.168.1.51:8011/upload/
# 升级方式 automatic 自动升级 manual 手动升级
VUE_APP_UPGRADE=manual

APP.vue实现

<!--
 * @Description: 
 * @Author: lanchao
 * @Date: 2022-04-14 11:21:23
 * @LastEditTime: 2022-05-28 17:38:12
 * @LastEditors: lanchao
 * @Reference: 
-->
<template>
  <div id="app">
    <router-view />
    <!--异步组件-->
    <Suspense v-if="isUpgrade">
      <template #default>
        <UpgradeComponent />
      </template>
      <template #fallback>
        <h1>检查更新 ...</h1>
      </template>
    </Suspense>
  </div>
</template>
<script lang="ts">
import { Options, Vue } from 'vue-class-component'
import { defineAsyncComponent } from 'vue'
@Options({
  components: {
    //异步加载组件
    UpgradeComponent: defineAsyncComponent(
      () => import('@/components/Upgrade.vue')
    )
  }
})
export default class AppComponent extends Vue {
  isUpgrade = process.env.IS_ELECTRON
    ? process.env.IS_ELECTRON &&
      process.env.NODE_ENV === 'production' &&
      process.env.VUE_APP_UPLOAD !== ''
    : false //判断是否可更新 true是 false否
}
</script>
<style lang="scss">
body {
  margin: 0;
  padding: 0;
}
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  font-size: 16px;
}
</style>

更新对话框

<!--手动升级-->
<template>
  <div id="update">
    <el-dialog
      :title="title"
      v-model="dialogVisible"
      width="60%"
      :close-on-click-modal="closeOnClickModal"
      :close-on-press-escape="closeOnPressEscape"
      :show-close="showClose"
      center
    >
      <div class="percentages">
        <el-progress
          status="success"
          :text-inside="true"
          :stroke-width="20"
          :percentage="percentage"
          :width="strokeWidth"
          :show-text="true"
        ></el-progress>
      </div>
      <div class="footer" v-if="btnStatus && !isBtn">
        <el-button @click="closeDialog">暂不升级</el-button>
        <el-button @click="upgrade" type="primary">升级</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script lang="ts">
import { Options, Vue } from 'vue-class-component'
import { ipcRenderer } from 'electron'
@Options({
  components: {}
})
export default class UpgradeComponent extends Vue {
  title = ''
  dialogVisible = false
  closeOnClickModal = false
  closeOnPressEscape = false
  showClose = false
  percentage = 0
  strokeWidth = 200
  timeOut = 0
  btnStatus = false //是否显示升级按钮
  isBtn = process.env.VUE_APP_UPGRADE === 'automatic' ? true : false //true 自动升级 false 手动升级
  /**
   * 关闭
   */
  closeDialog() {
    this.title = ''
    this.dialogVisible = false
  }
  /**
   * 开始更新
   */
  upgrade() {
    ipcRenderer.send('downloadUpdate')
    this.btnStatus = false
  }
  mounted() {
    ipcRenderer.send('checkForUpdate') //检查版本更新
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    let _this = this
    //接收主进程版本更新消息
    ipcRenderer.on('message', (event: any, arg: any) => {
      _this.title = arg.title
      if ('update-available' === arg.cmd) {
        _this.btnStatus = true
        _this.dialogVisible = true //显示升级对话框
      } else if ('download-progress' === arg.cmd) {
        const percent = Math.round(parseFloat(arg.message.percent))
        _this.percentage = percent
      } else if ('update-downloaded' === arg.cmd) {
        _this.dialogVisible = false
      } else if ('error' === arg.cmd) {
        _this.dialogVisible = false
      }
    })
  }
}
</script>
<style lang="scss">
#update {
  background: #f8f8f8;
}
.percentages {
  width: 100%;
  height: 5vh;
  line-height: 5vh;
  text-align: center;
}
body {
  margin: 0px;
}
.v-modal {
  opacity: 1 !important;
  background: rgba(0, 0, 0, 0.5) !important;
}
.el-tooltip {
  display: flex;
  align-items: center;
}
.footer {
  display: flex;
  flex-flow: row-reverse;
}
</style>
<style lang="scss" scoped>
.tableList {
  widows: 150px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.el-tooltip__popper {
  font-size: 14px;
  max-width: 300px !important;
  text-align: justify;
  text-justify: newspaper;
  word-break: break-all;
  line-height: 20px;
}
</style>

将打包后的dist_electron 目录下的 exe文件 和 latest.yml上传到 服务器VUE_APP_UPLOAD

解决打包后白屏

const router = createRouter({
  // history: createWebHistory(process.env.BASE_URL),
  history: process.env.IS_ELECTRON
    ? createWebHashHistory(process.env.BASE_URL)
    : createWebHistory(process.env.BASE_URL), //解决打包后白屏
  routes
})

更换图标

window

如下方式可解决打包后窗口左上角图标不显示问题

修改vue.config.js

"win": {

"icon": "build/icons/icon.ico" (为256*256图片)

}

mac

图标格式icns

推荐在线转换格式

demo地址 zhzy-tveep/src/components at main · Lateautumn00/zhzy-tveep · GitHub

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值