Vite5+Electron聊天室|electron31跨平臺仿微信EXE客戶端|vue3聊天程式

xiaoyan2017發表於2024-07-09

基於electron31+vite5+pinia2跨端仿微信Exe聊天應用ViteElectronChat

electron31-vite5-chat原創研發vite5+electron31+pinia2+element-plus跨平臺實戰仿微信客戶端聊天應用。實現了聊天、聯絡人、收藏、朋友圈/短影片等模組。支援electron多開視窗管理、桌布皮膚、自定義最大化/最小化/關閉等功能。

使用技術

  • 編輯器:vscode
  • 技術框架:electron31.1.0+vite5.3.1+vue3.4.29+vue-router4.4.0
  • 元件庫:element-plus^2.7.6 (餓了麼桌面端vue3元件庫)
  • 狀態管理:pinia^2.1.7
  • 儲存服務:pinia-plugin-persistedstate^3.2.1
  • 打包構建:electron-builder^24.13.3
  • electron整合vite外掛:vite-plugin-electron^0.28.7

專案結構目錄

electron-vitechat跨平臺聊天專案採用 electron31 整合 vite5.x 構建工具開發專案。

vite.js整合electron建立專案模板

  • vite5構建工具建立vue3專案模板
yarn create vite electron-vitechat
cd electron-vitechat
yarn install
yarn dev

至此一個簡單的vite5+vue3專案模板就搭建好了。

接下來,需要在專案中安裝一些electron依賴包。如果在安裝過程中卡住或失敗,建議多試幾次或切換淘寶映象源。

// 安裝electron
yarn add -D electron
// 安裝electron-builder 用於打包可安裝exe程式和綠色版免安裝exe程式
yarn add -D electron-builder
// 安裝vite-plugin-electron 用於將vite與electron無縫結合
yarn add -D vite-plugin-electron

至於具體如何整合vite和electron開發環境,大家可以去看看介紹文件。

https://github.com/electron-vite/vite-plugin-electron

electron-vitechat聊天專案已經同步到我的原創作品集,感興趣的可以去瞅瞅。

https://gf.bilibili.com/item/detail/1106312011

electron主執行緒/預載入檔案

/**
 * electron主程序入口配置
 * @author andy
 */

import { app, BrowserWindow } from 'electron'

import { WindowManager } from '../src/windows/index.js'

// 忽略安全警告提示 Electron Security Warning (Insecure Content-Security-Policy)
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = true

const createWindow = () => {
  let win = new WindowManager()
  win.create({isMajor: true})
  // 系統托盤管理
  win.trayManager()
  // 監聽ipcMain事件
  win.ipcManager()
}

app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    if(BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

app.on('window-all-closed', () => {
  if(process.platform !== 'darwin') app.quit()
})
/**
 * electron預載入檔案封裝
 * @author andy
 */

import { contextBridge, ipcRenderer } from 'electron'

contextBridge.exposeInMainWorld(
  'electron',
  {
    // 透過 channel 向主程序傳送非同步訊息。主程序使用 ipcMain.on() 監聽 channel
    send: (channel, args) => {
      ipcRenderer.send(channel, args)
    },

    // 透過 channel 向主程序傳送訊息,並非同步等待結果。主程序應該使用 ipcMain.handle() 監聽 channel
    invoke: (channel, args) => {
      return new Promise(resolve => ipcRenderer.invoke(channel, args).then(data => resolve(data)).catch(e => console.log(e)))
    },

    // 監聽 channel 事件
    on: (channel, func) => {
      console.log('receive event')
      ipcRenderer.on(channel, (event, ...args) => func(event, ...args))
    },

    // 一次性監聽事件
    once: (channel, func) => {
      ipcRenderer.once(channel, (event, ...args) => func(event, ...args))
    },

    setTitle: (title) => ipcRenderer.send('win-setTitle', title)
  }
)

專案公共模板

整個專案分為左側選單欄+側邊欄+右側內容區(自定義導航條)幾個大模組。

<template>
  <template v-if="!route?.meta?.isNewWin">
    <div
      class="vu__container flexbox flex-alignc flex-justifyc"
      :style="{'--themeSkin': appstate.config.skin}"
    >
      <div class="vu__layout flexbox flex-col">
        <div class="vu__layout-body flex1 flexbox" @contextmenu.prevent>
          <!-- 選單欄 -->
          <slot v-if="!route?.meta?.hideMenuBar" name="menubar">
            <MenuBar />
          </slot>

          <!-- 側邊欄 -->
          <div v-if="route?.meta?.showSideBar" class="vu__layout-sidebar flexbox">
            <aside class="vu__layout-sidebar__body flexbox flex-col">
              <slot name="sidebar">
                <SideBar />
              </slot>
            </aside>
          </div>

          <!-- 主內容區 -->
          <div class="vu__layout-main flex1 flexbox flex-col">
            <ToolBar v-if="!route?.meta?.hideToolBar" />
            <router-view v-slot="{ Component, route }">
              <keep-alive>
                <component :is="Component" :key="route.path" />
              </keep-alive>
            </router-view>
          </div>
        </div>
      </div>
    </div>
  </template>
  <template v-else>
    <WinLayout />
  </template>
</template>

electron+vite自定義無邊框拖拽視窗

整個專案採用無邊框 frame: false 模式,採用自定義拖拽導航條。

<script setup>
  import { ref } from 'vue'
  import { isTrue } from '@/utils'

  import { winSet } from '@/windows/actions'

  import Winbtns from './btns.vue'

  const props = defineProps({
    // 標題
    title: {type: String, default: ''},
    // 標題顏色
    color: String,
    // 背景色
    background: String,
    // 標題是否居中
    center: {type: [Boolean, String], default: false},
    // 是否固定
    fixed: {type: [Boolean, String], default: false},
    // 背景是否鏤空
    transparent: {type: [Boolean, String], default: false},
    // 層級
    zIndex: {type: [Number, String], default: 2024},

    /* 控制Winbtn引數 */
    // 視窗是否可最小化
    minimizable: {type: [Boolean, String], default: true},
    // 視窗是否可最大化
    maximizable: {type: [Boolean, String], default: true},
    // 視窗是否可關閉
    closable: {type: [Boolean, String], default: true},
  })
</script>

<template>
  <div class="ev__winbar" :class="{'fixed': fixed || transparent, 'transparent': transparent}">
    <div class="ev__winbar-wrap flexbox flex-alignc vu__drag">
      <div class="ev__winbar-body flex1 flexbox flex-alignc">
        <!-- 左側區域 -->
        <div class="ev__winbar-left"><slot name="left" /></div>
        <!-- 標題 -->
        <div class="ev__winbar-title" :class="{'center': center}">
          <slot name="title">{{title}}</slot>
        </div>
        <!-- 右側附加區域 -->
        <div class="ev__winbar-extra vu__undrag"><slot name="extra" /></div>
      </div>
      <Winbtns :color="color" :minimizable="minimizable" :maximizable="maximizable" :closable="closable" :zIndex="zIndex" />
    </div>
  </div>
</template>

electron+vue3新開多視窗

專案支援同時開啟多個視窗,呼叫公共封裝函式 winCreate 即可快速建立一個新視窗。

/**
 * 建立新視窗
 * @param {object} args 視窗配置引數 {url: '/about', width: 500, height: 300, ...}
 */
export function winCreate(args) {
  window.electron.send('win-create', args)
}
// 登入視窗
export function loginWindow() {
  winCreate({
    url: '/login',
    title: '登入',
    width: 320,
    height: 380,
    isMajor: true,
    resizable: false,
    maximizable: false,
    alwaysOnTop: true
  })
}

// 關於視窗
export function aboutWindow() {
  winCreate({
    url: '/win/about',
    title: '關於',
    width: 375,
    height: 300,
    minWidth: 375,
    minHeight: 300,
    maximizable: false,
    alwaysOnTop: true,
  })
}

// 設定視窗
export function settingWindow() {
  winCreate({
    url: '/win/setting',
    title: '設定',
    width: 550,
    height: 470,
    resizable: false,
    maximizable: false,
  })
}

支援如下視窗引數配置

// 自定義視窗引數
const windowOptions = {
  // 視窗唯一標識id
  id: null,
  // 視窗標題
  title: 'Electron-ViteChat',
  // 視窗路由地址
  url: '',
  // 視窗資料傳參
  data: null,
  // 是否是主視窗(為true則會關閉所有視窗並建立一個新視窗)
  isMajor: false,
  // 是否支援多開視窗(為true則支援建立多個視窗)
  isMultiple: false,
  // 視窗是否最大化
  maximize: false,
}

// 系統視窗引數(與electron的new BrowserWindow()引數一致)
const windowBaseOptions = {
  // 視窗圖示
  icon: join(__root, 'resources/shortcut.ico'),
  // 是否自動隱藏選單欄(按下Alt鍵顯示)
  autoHideMenuBar: true,
  // 視窗標題欄樣式
  titleBarStyle: 'hidden',
  // 視窗背景色
  backgroundColor: '#fff',
  // 寬度
  width: 840,
  // 高度
  height: 610,
  // 最小寬度
  minWidth: '',
  // 最小高度
  minHeight: '',
  // 視窗x座標
  x: '',
  // 視窗y座標
  y: '',
  // 是否可縮放
  resizable: true,
  // 是否可最小化
  minimizable: true,
  // 是否可最大化
  maximizable: true,
  // 是否可關閉
  closable: true,
  // 父視窗
  parent: null,
  // 是否模態視窗
  modal: false,
  // 視窗是否置頂
  alwaysOnTop: false,
  // 是否顯示視窗邊框(要建立無邊框視窗,將frame引數設定為false)
  frame: false,
  // 是否透明視窗(僅frame: false有效)
  transparent: false,
  // 建立時是否顯示視窗
  show: false,
}

electron建立系統托盤圖示

/** 
  * 系統托盤圖示管理
  */
trayManager() {
  console.log('create tray started...')

  if(this.tray) return
  const trayMenu = Menu.buildFromTemplate([
    {
      label: '開啟主介面',
      icon: join(__root, 'resources/tray-win.png'),
      click: () => {
        for(let i in this.winDict) {
          let win = this.getWinById(i)
          if(!win) return
          win.restore()
          win.show()
        }
      }
    },
    {
      label: '設定',
      icon: join(__root, 'resources/tray-setting.png'),
      click: () => this.sendByMainWin('win-ipcdata', {type: 'WINIPC_SETTINGWIN', value: null})
    },
    {
      label: '鎖定系統',
      click: () => null,
    },
    {
      label: '關閉托盤閃爍',
      click: () => this.trayFlash(false)
    },
    {
      label: '關於',
      click: () => this.sendByMainWin('win-ipcdata', {type: 'WINIPC_ABOUTWIN', value: null})
    },
    {
      label: '退出聊天室',
      icon: join(__root, 'resources/tray-exit.png'),
      click: () => {
        dialog.showMessageBox(this.winMain, {
          title: '提示',
          message: '確定要退出聊天程式嗎?',
          buttons: ['取消', '最小化托盤', '確認退出'],
          type: 'error',
          noLink: false,
          cancelId: 0,
        }).then(res => {
          // console.log(res)
          const index = res.response
          if(index == 0) {
            console.log('使用者取消操作')
          }else if(index == 1) {
            console.log('最小化到托盤')
            this.winMain.hide()
          }else if(index == 2) {
            console.log('退出程式')
            click: () => this.sendByMainWin('win-ipcdata', {type: 'WINIPC_LOGOUT', value: null})
            app.quit()
          }
        })
      }
    }
  ])
  this.tray = new Tray(this.trayIcon)
  this.tray.setContextMenu(trayMenu)
  this.tray.setToolTip(app.name)
  this.tray.on('double-click', () => {
    console.log('tray double clicked!')
  })
}

注意:如果路徑配置不正確,則無法顯示托盤圖示。

// 當前目錄路徑
const __dirname = import.meta.dirname
// 根目錄
const __root = join(__dirname, '../../')

electron專案打包配置

在專案根目錄新建一個 electron-builder.json 打包配置檔案。

{
  "productName": "Electron-ViteChat",
  "appId": "com.andy.electron-vite-wechat",
  "copyright": "Copyright © 2024-present Andy  Q:282310962",
  "compression": "maximum",
  "asar": true,
  "directories": {
    "output": "release/${version}"
  },
  "nsis": {
    "oneClick": false,
    "allowToChangeInstallationDirectory": true,
    "perMachine": true,
    "deleteAppDataOnUninstall": true,
    "createDesktopShortcut": true,
    "createStartMenuShortcut": true,
    "shortcutName": "ElectronViteChat"
  },
  "win": {
    "icon": "./resources/shortcut.ico",
    "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}",
    "target": [
      {
        "target": "nsis",
        "arch": ["ia32"]
      }
    ]
  },
  "mac": {
    "icon": "./resources/shortcut.icns",
    "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"
  },
  "linux": {
    "icon": "./resources",
    "artifactName": "${productName}-v${version}-${platform}-${arch}-setup.${ext}"
  }
}

OK,綜上就是electron31+vite5開發桌面端聊天系統的一些知識分享,希望對大家有所幫助!

最後附上兩個最新flutter實戰專案例項

https://www.cnblogs.com/xiaoyan2017/p/18234343

https://www.cnblogs.com/xiaoyan2017/p/18092224

相關文章