electron-vue构建聊天桌面应用开发实践分享

一,主进程和渲染进程之间的传参

1,主进程向渲染进程传参和接收参数

import { BrowserWindow, ipcMain  } from 'electron'

mainWindow = new BrowserWindow({
    // ...
})
// 主动发送给渲染进程
function sendMessage(code, data) {
    mainWindow.webContents.send('new-message', { code, data })
}
// 接收渲染进程发过来的消息
ipcMain.on('new-message', function(event, arg) {
    console.log('渲染线程发过来的消息:', arg)
    switch (arg.code) {
        case 'ping':  
                sendMessage('reply', {})
                //发送给渲染进程也可以这样做 
                // event.sender.send('new-message', {code: 'reply', {}}) 
            break;
    }
})

2,渲染进程向主进程传参和接收主进程的传参

import { ipcRenderer } from 'electron'

export default {
    
    mounted: function () {
        ipcRenderer.on('new-message', function(e, data) {
            console.log('主进程发过来的消息:', e, data)
            switch(data.code) {
                case 'reply':
                    // 逻辑
                break;
            }
        })
    },
    methods: {
        clickBtn(){
            ipcRenderer.send('new-message', {code:'ping',data:{}})  // 发送给主线程
        }
    }
}

二,缩小,放大,关闭,全屏按钮的替换

系统默认的三个按钮样式不好看,不是我们想要的,故禁止显示默认后自己用样式写了一个来实现他们对应的功能。
1,先隐藏默认的系统菜单。

import { BrowserWindow, ipcMain  } from 'electron'

mainWindow = new BrowserWindow({
    height: 640,
    width: 960,
    useContentSize: true,
    frame: false,  // 隐藏默认的系统菜单
})

2,页面布局按钮样式,添加事件传给主进程告知做对应的放大缩小等操作。

<template>
    <div class="menu-tool">
        <div class="win-control">
            <ul class="control-ul">
                <li class="control-li" key="0" @click="clickWinBtn('shrink')">
                    <i class="iconfont">&#xe6b7;</i>
                </li>
                <li class="control-li" key="1" @click="clickWinBtn('magnify')" v-show="!showfullIcon">
                    <i class="iconfont">&#xe6ae;</i>
                </li>
                <li class="control-li" key="2" @click="clickWinBtn('full')" v-show="showfullIcon">
                    <i class="iconfont">&#xe6c2;</i>
                </li>
                <li class="control-li" key="3" @click="clickWinBtn('close')">
                    <i class="iconfont">&#xe6b8;</i>
                </li>
            </ul>
        </div>
    </div>
</template>

<script>

import { ipcRenderer } from 'electron'

export default {
    props: {
        
    },
    components: {

    },
    data: function () {
        return {
            showfullIcon: false  // 显示全屏的按钮
        }
    },
    computed: {
    },
    mounted: function () {
        ipcRenderer.on('new-message', (event, { code, data }) => {
            switch (code) {
                case 'show-full-icon':
                        this.showfullIcon = true
                    break;
                case 'hide-full-icon':
                        this.showfullIcon = false
                    break;
            }
        })
    },
    methods: {
        clickWinBtn(string) {
            ipcRenderer.send('new-message', {code:string})
        }
    }
}
</script>

3,主进程收到渲染进程的事件控制窗口

function sendMessage(code, data) {
    mainWindow.webContents.send('new-message', { code, data })
}
ipcMain.on('new-message', function(event, arg) {
    switch (arg.code) {
        case 'shrink':
                // 缩小
                mainWindow.minimize()
            break;
        case 'magnify':
                // 放大
                mainWindow.maximize()
                sendMessage('show-full-icon')
            break;
        case 'close':
                // 关闭
                mainWindow.hide()
            break;
        case 'full':
                // 退出全屏
                mainWindow.unmaximize()
                sendMessage('hide-full-icon')
            break;
    }
})

三,注册系统快捷键功能

我们按Ctrl+F12打开调试工具

import {globalShortcut } from 'electron'

globalShortcut.register('CommandOrControl+F12', () => {
    mainWindow.webContents.openDevTools()
})

四,在电脑任务栏右下角显示托盘小图标,并让他能实现消息提醒闪烁的功能。

1,先禁止点击窗口关闭按钮时退出了APP。

import { app, BrowserWindow } from 'electron'

let mainWindow, quit = false
mainWindow = new BrowserWindow({
    //...
})
//关闭窗口
mainWindow.on('close', (event) => {
    mainWindow.hide()
    mainWindow.setSkipTaskbar(true)  // 任务栏不显示窗口
    if (!quit) event.preventDefault() // 阻止默认事件退出APP
})

// 退出app
function quitApp() {
    quit = true
    app.quit()
}

2,显示托盘并给它鼠标右键的菜单,和点击托盘显示

import { Tray, Menu } from 'electron'
import path from 'path'

let appIcon = null
function tray() {
    appIcon = new Tray(path.join(__static, 'icon.ico'))
    let contextMenu = Menu.buildFromTemplate([{
            label: '打开 APP',
            click: function(e) {
                mainWindow.show()
                mainWindow.setSkipTaskbar(false)
            }
        },{
            label: '退出 APP',
            click: function(e) {
                quitApp()
            }
        }]);

    appIcon.setToolTip('APP名称');
    appIcon.setContextMenu(contextMenu)

    appIcon.on('click', function() {
        mainWindow.show()
        mainWindow.setSkipTaskbar(false)
    })
}

3,加闪烁功能。
3 - 1,渲染进程页面上写两个按钮,控制托盘图标的闪烁

<template>
    <div class="page-main">
        <button key="1" @click="openFlicker">闪烁图标</button>
        <button key="2" @click="closeFlicker">暂停闪烁</button>
    </div>
</template>

<script>

import { ipcRenderer } from 'electron'

export default {
    data: function () {
        return {
            
        }
    },
    computed: {
    },
    mounted: function () {
    },
    methods: {
        openFlicker() {
            ipcRenderer.send('open-flicker')
        },
        closeFlicker() {
            ipcRenderer.send('close-flicker')
        }
    }
}
</script>

<style lang="scss" scoped>
    
</style>

3 - 2,主进程控制托盘小图标。思路是控制两张图片的交替显示,一张可以是透明的图片,从而起到闪烁效果。

import path from 'path'

let timer = null
ipcMain.on('open-flicker', function(event, arg) {
    if (timer) return
    let count = 0
    timer = setInterval(function() {
        count++
        if (count % 2 === 0) {
            appIcon.setImage(path.join(__static, 'icon.ico'))
        } else {
            appIcon.setImage(path.join(__static, 'favicon.ico'))
        }
    }, 500)
})
ipcMain.on('close-flicker', function(event, arg) {
    clearInterval(timer)
    timer = null
    appIcon.setImage(path.join(__static, 'icon.ico'))
})

五,下载文件或打开文件以系统默认的打开方式。

文档连接https://www.electronjs.org/docs/api/download-item
1,页面上加个打开文件的事件

openFile(url) {
    ipcRenderer.send('new-message', {
        code:'load-and-look',
        data:{
            filePath: url // 'www.网址/office.docx'
        }
    })
}

2,主进程收到文件的网络路径后先判断本地是否有改文件,有的话打开,没有的话下载后再打开。

import {app, session, shell } from 'electron'
import fs from 'fs'

// 发送给渲染进程
function sendMessage(code, data) {
    mainWindow.webContents.send('new-message', { code, data })
}
// 接收渲染进程发过来的消息
ipcMain.on('new-message', function(event, arg) {
    switch (arg.code) {
        case 'load-and-look':
                loadFile(arg.data)
            break;
    }
})
// 下载文件 和查看文件
let savePath = app.getPath('downloads') // 保存的路径,自定义
function loadFile(file) {
    let pathList = file.filePath.split('/')
    let fileName = pathList[pathList.length - 1]
   
    fs.exists(`${savePath}\\${fileName}`,(exists)=> {
        console.log(exists)
        if (exists) {
            shell.openItem(`${savePath}\\${fileName}`)
        }else {
            mainWindow.webContents.downloadURL(file.filePath)
        }
    })
}
// 监听下载
session.defaultSession.on('will-download', (event, item, webContents) => {
    //设置文件存放位置
    item.setSavePath(`${savePath}\\${item.getFilename()}`);

    item.on('updated', (event, state) => {
        if (state === 'interrupted') {
            console.log('Download is interrupted but can be resumed')
        } else if (state === 'progressing') {
            if (item.isPaused()) {
                console.log('下载暂停')
            } else {
                let loadRate = item.getReceivedBytes() / item.getTotalBytes()
                mainWindow.webContents.send('load-rate', { data: loadRate })
                // console.log(`已经取回的字节: ${item.getReceivedBytes()}`)
            }
        }
    })
    item.once('done', (event, state) => {
        if (state === 'completed') {
            console.log('下载成功')
            shell.openItem(`${savePath}\\${item.getFilename()}`)
        } else {
            console.log(`下载失败: ${state}`)
        }
    })
})

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335