electron聊天室|vue+electron-vue仿微信客户端|electron桌面聊天
ztj100 2024-12-27 17:54 22 浏览 0 评论
一、项目概况
基于Electron+vue+electron-vue+vuex+Nodejs+vueVideoPlayer+electron-builder等技术仿制微信电脑端界面聊天室实例,实现消息发送/动态表情,图片/视频预览,拖拽图片/粘贴截图发送,朋友圈/红包/换肤等功能。
二、效果图
三、技术栈
- 框架技术:electron + electron-vue + vue
- 状态管理:Vuex
- 地址路由:Vue-router
- 字体图标:阿里iconfont字体图标库
- 弹窗插件:wcPop
- 打包工具:electron-builder
- 环境配置:Node.js + Chromium
- 图片预览:vue-photo-preview
- 视频组件:vue-video-player
如何配置开发环境及使用electron-vue,这里不作多介绍,可查阅官网及搜资料
https://simulatedgreg.gitbooks.io/electron-vue/content/cn/
https://github.com/SimulatedGREG/electron-vue
注意:由于electron-vue作者长时间未更新,里面electron版本v2.0.4太旧,如遇问题,可升级到最新版本
◆ Electron主进程index.js
通过BrowserWindow创建和控制浏览器窗口,官网有详细介绍,这里略过...
https://electronjs.org/docs/api/browser-window
...
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()
}
})
...
如上图:创建托盘图标及闪烁效果
/**
* 托盘图标事件
*/
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;}
注意:默认设置-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 等表情符标签,展示信息的时候解析标签就行
<!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
思路:新建一个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实现微信截图功能
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开发仿微信客户端聊天实例分享,希望能有些帮助!!
最后附上两个最近实例项目
uniapp+vue实现抖音短视频|陌陌app直播:https://www.cnblogs.com/xiaoyan2017/p/11835641.html
taro+react多端 (h5/小程序/App) 聊天实例:https://www.cnblogs.com/xiaoyan2017/p/12039544.html
本文为博主原创文章,未经博主允许不得转载,欢迎大家一起交流 QQ(282310962) wx(xy190310)
相关推荐
- sharding-jdbc实现`分库分表`与`读写分离`
-
一、前言本文将基于以下环境整合...
- 三分钟了解mysql中主键、外键、非空、唯一、默认约束是什么
-
在数据库中,数据表是数据库中最重要、最基本的操作对象,是数据存储的基本单位。数据表被定义为列的集合,数据在表中是按照行和列的格式来存储的。每一行代表一条唯一的记录,每一列代表记录中的一个域。...
- MySQL8行级锁_mysql如何加行级锁
-
MySQL8行级锁版本:8.0.34基本概念...
- mysql使用小技巧_mysql使用入门
-
1、MySQL中有许多很实用的函数,好好利用它们可以省去很多时间:group_concat()将取到的值用逗号连接,可以这么用:selectgroup_concat(distinctid)fr...
- MySQL/MariaDB中如何支持全部的Unicode?
-
永远不要在MySQL中使用utf8,并且始终使用utf8mb4。utf8mb4介绍MySQL/MariaDB中,utf8字符集并不是对Unicode的真正实现,即不是真正的UTF-8编码,因...
- 聊聊 MySQL Server 可执行注释,你懂了吗?
-
前言MySQLServer当前支持如下3种注释风格:...
- MySQL系列-源码编译安装(v5.7.34)
-
一、系统环境要求...
- MySQL的锁就锁住我啦!与腾讯大佬的技术交谈,是我小看它了
-
对酒当歌,人生几何!朝朝暮暮,唯有己脱。苦苦寻觅找工作之间,殊不知今日之事乃我心之痛,难道是我不配拥有工作嘛。自面试后他所谓的等待都过去一段时日,可惜在下京东上的小金库都要见低啦。每每想到不由心中一...
- MySQL字符问题_mysql中字符串的位置
-
中文写入乱码问题:我输入的中文编码是urf8的,建的库是urf8的,但是插入mysql总是乱码,一堆"???????????????????????"我用的是ibatis,终于找到原因了,我是这么解决...
- 深圳尚学堂:mysql基本sql语句大全(三)
-
数据开发-经典1.按姓氏笔画排序:Select*FromTableNameOrderByCustomerNameCollateChinese_PRC_Stroke_ci_as//从少...
- MySQL进行行级锁的?一会next-key锁,一会间隙锁,一会记录锁?
-
大家好,是不是很多人都对MySQL加行级锁的规则搞的迷迷糊糊,一会是next-key锁,一会是间隙锁,一会又是记录锁。坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如何用命令分析加...
- 一文讲清怎么利用Python Django实现Excel数据表的导入导出功能
-
摘要:Python作为一门简单易学且功能强大的编程语言,广受程序员、数据分析师和AI工程师的青睐。本文系统讲解了如何使用Python的Django框架结合openpyxl库实现Excel...
- 用DataX实现两个MySQL实例间的数据同步
-
DataXDataX使用Java实现。如果可以实现数据库实例之间准实时的...
- MySQL数据库知识_mysql数据库基础知识
-
MySQL是一种关系型数据库管理系统;那废话不多说,直接上自己以前学习整理文档:查看数据库命令:(1).查看存储过程状态:showprocedurestatus;(2).显示系统变量:show...
- 如何为MySQL中的JSON字段设置索引
-
背景MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型。自此,它成了一种逃离严格列定义的方式,可以存储各种形状和大小的JSON文档,例如审计日志、配置信息、第三方数据包、用户自定...
你 发表评论:
欢迎- 一周热门
-
-
MySQL中这14个小玩意,让人眼前一亮!
-
旗舰机新标杆 OPPO Find X2系列正式发布 售价5499元起
-
【VueTorrent】一款吊炸天的qBittorrent主题,人人都可用
-
面试官:使用int类型做加减操作,是线程安全吗
-
C++编程知识:ToString()字符串转换你用正确了吗?
-
【Spring Boot】WebSocket 的 6 种集成方式
-
PyTorch 深度学习实战(26):多目标强化学习Multi-Objective RL
-
pytorch中的 scatter_()函数使用和详解
-
与 Java 17 相比,Java 21 究竟有多快?
-
基于TensorRT_LLM的大模型推理加速与OpenAI兼容服务优化
-
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)