electron+vue聊天室|electron-vue仿微信界面

6,000 阅读4分钟

最近一直在折腾electron技术,由于Electron 是一个使用 JavaScript, HTML 和 CSS 等 Web 技术创建原生程序的框架,可以使用vue开发页面,加上之前有使用vue开发过聊天实例,于是采用electron+vue技术选型项目开发。

electronVchat聊天基于electron+vue+electron-vue+vuex+electron-builder+vue-router等技术开发的仿微信桌面端聊天IM项目,有消息发送/表情,图片/视频预览,拖拽上传/粘贴截图发送/微信dll截图,右键菜单、朋友圈/红包/换肤等功能。

vue聊天室|h5+vue仿微信聊天界面|vue仿微信

技术栈:

  • 运用技术:electron + electron-vue + vue
  • 状态管理:Vuex
  • 地址路由:Vue-router
  • 字体图标:阿里iconfont字体图标库
  • 弹窗插件:wcPop
  • 打包工具:electron-builder
  • 图片预览:vue-photo-preview
  • 视频组件:vue-video-player

有关electron安装配置及electron-vue项目环境搭建,大家可以去参阅官网就行。

simulatedgreg.gitbooks.io/electron-vu…

github.com/SimulatedGR…

electron主进程创建窗口

electron-vue构建项目后,主进程入口页面是src/main/index.js

通过electron提供的BrowserWindow对象创建窗体,有关BrowserWindow详细配置可参看官方介绍:electronjs.org/docs/api/br…

/**
 * @Desc   主线程  Create by andy on 2019/12/26
 * @about   Q:282310962  wx:xy190310
 */

import { BrowserWindow, app, ipcMain, Tray, Menu } from 'electron'

// 引入主线程公共配置
import Common from './utils/common'

/**
 * Set `__static` path to static files in production
 * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
 */
if (process.env.NODE_ENV !== 'development') {
  global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}

let mainWin
let tray
let forceQuit = false
let logined = false

/**
 * 创建主窗口
 */
function createMainWin() {
  mainWin = new BrowserWindow({
    // 背景颜色
    // backgroundColor: '#ebebeb',
    width: Common.WIN_SIZE_MAIN.width,
    height: Common.WIN_SIZE_MAIN.height,
    title: Common.WIN_TITLE,
    useContentSize: true,
    autoHideMenuBar: true,
    // 无边框窗口
    frame: false,
    resizable: true,
    // 窗口创建的时候是否显示. 默认值为true
    show: false,
    webPreferences: {
      // devTools: false,
      webSecurity: false
    }
  })

  mainWin.setMenu(null)
  mainWin.loadURL(Common.WIN_LOAD_URL())

  mainWin.once('ready-to-show', () => {
    mainWin.show()
    mainWin.focus()
  })

  // 判断最小化到系统托盘
  mainWin.on('close', (e) => {
    if(logined && !forceQuit) {
      e.preventDefault()
      mainWin.hide()
    }else {
      mainWin = null
      app.quit()
    }
  })

  initialIPC()
}

app.on('ready', createMainWin)

app.on('activate', () => {
  if(mainWin === null) {
    createMainWin()
  }
})

app.on('before-quit', () => {
  forceQuit = true
})

app.on('window-all-closed', () => {
  if(process.platform !== 'darwin') {
    app.quit()
  }
})

...

electron实现托盘图标及闪烁效果

/**
 * 托盘图标事件
 */
let flashTrayTimer = null
let trayIco1 = `${__static}/icon.ico`
let trayIco2 = `${__static}/empty.ico`
let apptray = {
  // 创建托盘图标
  createTray() {
    tray = new Tray(trayIco1)
    const menu = Menu.buildFromTemplate([
      {
        label: '打开主界面',
        icon: `${__static}/tray-ico1.png`,
        click: () => {
          if(mainWin.isMinimized()) mainWin.restore()
          mainWin.show()
          mainWin.focus()

          this.flashTray(false)
        }
      },
      {
        label: '关于',
      },
      {
        label: '退出',
        click: () => {
          if(process.platform !== 'darwin') {
            mainWin.show()
            // 清空登录信息
            mainWin.webContents.send('clearLoggedInfo')

            forceQuit = true
            mainWin = null
            app.quit()
          }
        }
      },
    ])
    tray.setContextMenu(menu)
    tray.setToolTip('electron-vchat v1.0.0')

    // 托盘点击事件
    tray.on('click', () => {
      if(mainWin.isMinimized()) mainWin.restore()
      mainWin.show()
      mainWin.focus()

      this.flashTray(false)
    })
  },
  // 托盘图标闪烁
  flashTray(bool) {
    let hasIco = false

    if(bool) {
      if(flashTrayTimer) return
      flashTrayTimer = setInterval(() => {
        tray.setImage(hasIco ? trayIco1 : trayIco2)
        hasIco = !hasIco
      }, 500)
    }else {
      if(flashTrayTimer) {
        clearInterval(flashTrayTimer)
        flashTrayTimer = null
      }

      tray.setImage(trayIco1)
    }
  },
  // 销毁托盘图标
  destroyTray() {
    this.flashTray(false)
    tray.destroy()
    tray = null
  }
}

electron渲染进程renderer目录页面配置

引入公共组件、第三方组件及页面样式

/**
 * @Desc 公共及全局组件配置
 */

// 引入公共组件
import winBar from './components/winbar'
import sideBar from './components/sidebar'

// 引入公共样式
import './assets/fonts/iconfont.css'
import './assets/css/reset.css'
import './assets/css/layout.css'

// 引入弹窗wcPop
import wcPop from './assets/js/wcPop/wcPop'
import './assets/js/wcPop/skin/wcPop.css'

// 引入图片预览组件vue-photo-preview
import photoView from 'vue-photo-preview'
import 'vue-photo-preview/dist/skin.css'

// 引入视频播放组件vue-video-player
import videoPlayer from 'vue-video-player'
import 'video.js/dist/video-js.css'

const install = Vue => {
    // 注册组件
    Vue.component('win-bar', winBar)
    Vue.component('side-bar', sideBar)

    // 应用实例
    Vue.use(photoView, {
        // loop: false, //循环
        // fullscreenEl: true, //全屏
        // arrowEl: true, //左右按钮
    })
    Vue.use(videoPlayer)
}

export default install

electron中vuex使用和vue使用基本一样,这里不作介绍。

◆ electron自定义最大/小化、关闭按钮、无外框窗口

electron中配置frame: false后,窗口将以无边框展示,原先的顶部操作栏就没有了,需要自定义配置。

import { app, remote, ipcRenderer } from 'electron'
import { mapState, mapMutations } from 'vuex'

let currentWin = remote.getCurrentWindow()

export default {
    props: {
        title: String,
    },
    data () {
        return {// 是否置顶
            isAlwaysOnTop: false,
            // 窗口是否可以最小化
            isMinimizable: true,
            // 窗口是否可以最大化
            isMaximizable: true,
        }
    },
    computed: {
        ...mapState(['isWinMaxed'])
    },
    mounted() {if(!currentWin.isMinimizable()) {
            this.isMinimizable = false
        }
        if(!currentWin.isMaximizable()) {
            this.isMaximizable = false
        }
        if(this.isWinMaxed && currentWin.isMaximizable()) {
            currentWin.maximize()
        }

        // 监听是否最大化
        currentWin.on('maximize', () => {
            this.SET_WINMAXIMIZE(true)
        })
        currentWin.on('unmaximize', () => {
            this.SET_WINMAXIMIZE(false)
        })
    },
    methods: {
        ...mapMutations(['SET_WINMAXIMIZE']),

        // 置顶窗口
        handleFixTop() {
            this.isAlwaysOnTop = !this.isAlwaysOnTop
            currentWin.setAlwaysOnTop(this.isAlwaysOnTop)
        },

        // 最小化
        handleMin() {
            currentWin.minimize()
        },

        // 最大化
        handleMax() {
            if(!currentWin.isMaximizable()) return
            if(currentWin.isMaximized()) {
                currentWin.unmaximize()
                this.SET_WINMAXIMIZE(false)
            }else {
                currentWin.maximize()
                this.SET_WINMAXIMIZE(true)
            }
        },

        // 关闭
        handleQuit() {
            currentWin.close()
        }
    }
}

项目中使用的是设置-webkit-app-region: drag 进行局部拖动

_**注意:**默认设置-webkit-app-region: drag后,下面的元素不能点击操作,可通过设置需点击元素no-drag即可。_

electron图文聊天编辑器光标处插入表情+截图dll

vue中设置div可编辑contenteditable="true" 自定义双向绑定v-model ,定位光标处插入动态表情。

由于之前已经有过分享,这里不作介绍了

juejin.cn/post/684490…

最后分享个uniapp实例

基于vue+uniapp直播项目|uni-app仿抖音/陌陌直播室