欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

程序员文章站 2022-03-18 14:08:16
一、项目概况 基于Electron+vue+electron-vue+vuex+Nodejs+vueVideoPlayer+electron-builder等技术仿制微信电脑端界面聊天室实例,实现消息发送/动态表情,图片/视频预览,拖拽图片/粘贴截图发送,朋友圈/红包/换肤等功能。 二、效果图 三、 ......

一、项目概况

基于electron+vue+electron-vue+vuex+nodejs+vuevideoplayer+electron-builder等技术仿制微信电脑端界面聊天室实例,实现消息发送/动态表情,图片/视频预览,拖拽图片/粘贴截图发送,朋友圈/红包/换肤等功能。

二、效果图

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

三、技术栈

  • 框架技术:electron + electron-vue + vue
  • 状态管理:vuex
  • 地址路由:vue-router
  • 字体图标:阿里iconfont字体图标库
  • 弹窗插件:wcpop
  • 打包工具:electron-builder
  • 环境配置:node.js + chromium
  • 图片预览:vue-photo-preview
  • 视频组件:vue-video-player

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天 electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

如何配置开发环境及使用electron-vue,这里不作多介绍,可查阅官网及搜资料

https://github.com/simulatedgreg/electron-vue

注意:由于electron-vue作者长时间未更新,里面electron版本v2.0.4太旧,如遇问题,可升级到最新版本

◆ electron主进程index.js

通过browserwindow创建和控制浏览器窗口,官网有详细介绍,这里略过...

...

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()
apptray.createtray() } app.on('ready', createmainwin) app.on('activate', () => { if(mainwin === null) { createmainwin() } }) ...

electron聊天室|vue+electron-vue仿微信客户端|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(flash) {
    let hasico = false

    if(flash) {
      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
  }
}

◆ 渲染进程主页面main.js及app.vue

/**
 * @desc   主入口main.js
 * @about  q:282310962  wx:xy190310
 */

import vue from 'vue'
import axios from 'axios'

import app from './app'
import router from './router'
import store from './store'

// 引入组件配置
import $components from './components'
vue.use($components)

if (!process.env.is_web) vue.use(require('vue-electron'))
vue.http = vue.prototype.$http = axios


/* eslint-disable no-new */
new vue({
  components: { app },
  router,
  store,
  template: '<app/>'
}).$mount('#app')
<template>
  <div id="app">
    <div class="elv-container" :style="$store.state.winskin && {'background-image': 'url('+$store.state.winskin+')'}">
      <div class="elv-wrapper flexbox">
        <!-- //侧边栏 -->
        <side-bar v-if="!$route.meta.hidesidebar" />

        <!-- //主布局 -->
        <div class="elv-mainbx flex1 flexbox flex-col">
          <!-- ...顶部按钮 -->
          <win-bar />
          
          <keep-alive>
            <router-view></router-view>
          </keep-alive>
        </div>
      </div>
    </div>
  </div>
</template>

至于状态管理及路由配置基本和vue里面使用一样,这里也略过...

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

配置browserwindow里面frame:false就会是无边框窗口,这时就可以自定义最大/小,关闭按钮,那么问题来了,无边框窗口如何进行拖动尼?

  • 1、通过mousedown、mousemove等事件处理
  • 2、设置需要拖动区css属性 -webkit-app-region
.elv__drag{-webkit-app-region: drag; -webkit-user-select:none; -moz-user-select:none; -ms-user-select:none; user-select:none;}
.elv__nodrag{-webkit-app-region: no-drag;}

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

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

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()
        }
    }
}

◆ 聊天编辑器光标处插入表情、div可编辑contenteditable="true"双向绑定

如何实现electron vue中向编辑框contenteditable光标处插入动态表情,类似qq、微信聊天编辑器??

  • 1、使用input、textarea文本框实现

通过给input或textarea文本框插入[奋斗]、(:17 等表情符标签,展示信息的时候解析标签就行

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
        <div class="container">
            <div class="row">
                <div class="col col-sm-12">
                    <button class="btn btn-success" data-emoj="[笑脸]">[笑脸]</button>
                    <button class="btn btn-success" data-emoj="[奋斗]">[奋斗]</button>
                    <button class="btn btn-success" data-emoj="[:17]">[:17]</button>
                </div>
                <div class="col col-sm-12">
                    <textarea class="form-control" id="content" rows="10"></textarea>
                </div>
            </div>
        </div>
     
        <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
        <script>
            (function ($) {
                $.fn.extend({
                    insertemojatcaret: function (myvalue) {
                        var $t = $(this)[0];
                        if (document.selection) {
                            this.focus();
                            sel = document.selection.createrange();
                            sel.text = myvalue;
                            this.focus();
                        } else if ($t.selectionstart || $t.selectionstart == '0') {
                            var startpos = $t.selectionstart;
                            var endpos = $t.selectionend;
                            var scrolltop = $t.scrolltop;
                            $t.value = $t.value.substring(0, startpos) + myvalue + $t.value.substring(endpos, $t.value.length);
                            this.focus();
                            $t.selectionstart = startpos + myvalue.length;
                            $t.selectionend = startpos + myvalue.length;
                            $t.scrolltop = scrolltop;
                        } else {
                            this.value += myvalue;
                            this.focus();
                        }
                    }
                });
            })(jquery);
                 
            $("button").on("click", function() {
                $("#content").insertemojatcaret($(this).attr("data-emoj"));
            });
        </script>
    </body>
</html>
  • 2、运用h5中div可编辑contenteditable="true"实现

在vue页面设置contenteditable="true" 实现富文本文本框效果,由于div不能绑定v-model,只能使用vue提供的自定义组件v-model功能。

https://cn.vuejs.org/v2/guide/components-custom-events.html#%e8%87%aa%e5%ae%9a%e4%b9%89%e7%bb%84%e4%bb%b6%e7%9a%84-v-model

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

思路:新建一个chatinputedit.vue组件,通过监听数据变化返回父组件数据

1):调用chatinputedit.vue组件,并给其绑定v-model

<template>
    ...
    <chatinputedit ref="chatinput" v-model="editortext" />
</template>

...

export default {
    data () {
        return {
            editortext: '',
            
            ...
        }
    },
    ...
}

2):v-model中传入的值在子组件prop中获取并监听value变化

export default {
    props: {
        value: { type: string, default: '' }
    },
    data () {
        return {
            editortext: this.value,
            ...
        }
    },
    watch: {
        value() {
            ...
        }
    },
}

3):监听获取到的值赋值给子组件中的v-html参数,就打通双向绑定链路了

/**
 * contenteditable光标处插入内容
 */
inserthtmlatcaret(html) {
    let sel, range;
    if(!this.$refs.editor.childnodes.length) {
        this.$refs.editor.focus()
    }
    if (window.getselection) {
        // ie9 and non-ie
        sel = window.getselection();

        if (sel.getrangeat && sel.rangecount) {
            range = sel.getrangeat(0);
            range.deletecontents();
            let el = document.createelement("div");
            el.appendchild(html)
            var frag = document.createdocumentfragment(), node, lastnode;
            while ((node = el.firstchild)) {
                lastnode = frag.appendchild(node);
            }
            range.insertnode(frag);
            if (lastnode) {
                range = range.clonerange();
                range.setstartafter(lastnode);
                range.collapse(true);
                sel.removeallranges();
                sel.addrange(range);
            }
        }
    } else if (document.selection && document.selection.type != "control") {
        // ie < 9
        document.selection.createrange().pastehtml(html);
    }
}

◆ electron+vue实现微信截图功能

electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天

node中通过的execfile方法执行exe文件,exe调用同级目录下的微信截图dll,调出截图工具

handlecapturescreen() {
    return new promise((resolve) => {
        const { execfile } = require('child_process')
        var screenwin = execfile('./static/printscr.exe')
        screenwin.on('exit', function(code) {
            let pngs = require('electron').clipboard.readimage().topng()
            let imgdata = new buffer.from(pngs, 'base64')
            let imgs = 'data:image/png;base64,' + btoa(new uint8array(imgdata).reduce((data, byte) => data + string.fromcharcode(byte), ''))
            resolve(imgs)
        })
    })
},

okay。以上就是基于electron+vue开发仿微信客户端聊天实例分享,希望能有些帮助!!