原创tauri2.0+vue3+pinai2仿QQ/微信客户端聊天Exe程序TauriWinChat。
tauri2-vue3-winchat自研vite5+tauri2.0+vue3 setup+element-plus跨平台仿QQ|微信桌面端聊天软件。全新封装tauri2多开窗口管理、自定义圆角阴影窗体。实现聊天、通讯录、收藏、我的、朋友圈/小视频等模块。
技术栈
- 编码器:VScode
- 技术框架:tauri2.0+vite^5.4.7+vue^3.5.8+vue-router^4.4.5
- 状态管理:pinia^2.2.2
- 本地存储:pinia-plugin-persistedstate^4.0.2
- UI组件库:element-plus^2.8.3
- 富文本编辑器:@vueup/vue-quill^1.2.0
- 样式预处理:sass^1.79.3
- 小视频滑动组件:swiper^11.1.14
项目框架结构
vue3-taurichat桌面聊天项目采用最新跨平台框架 tauri2.0 整合 vite5 搭建项目模板。
Tauri2.0-Vue3chat聊天程序已经同步到我的原创作品集,有需要的可以去看看~
https://gf.bilibili.com/item/detail/1107133011
vite.js整合tauri2.0创建多窗口|自定义托盘闪烁右键菜单
之前有分享过一篇介绍tauri2结合vite.js搭建桌面端项目、创建多窗口、自定义托盘闪烁及右键菜单。感兴趣的可以去瞅瞅。
https://www.cnblogs.com/xiaoyan2017/p/18416811
tauri2-vue3chat实现类似QQ登录/主窗口切换,支持主题壁纸、置顶窗口、自定义最大化/最小化/关闭按钮,聊天模块支持图片/视频新窗口预览、拖拽图片到聊天区域。
主入口main.js
/** * 主入口文件main.js */ import { createApp } from 'vue' import './style.scss' import App from './App.vue' // 引入组件库 import VEPlus from 've-plus' import 've-plus/dist/ve-plus.css' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' // 引入路由/状态管理 import Router from './router' import Pinia from './pinia' createApp(App) .use(VEPlus) .use(ElementPlus) .use(Router) .use(Pinia) .mount('#app')
Tauri2.0-Vue3chat布局模板
项目整体分为菜单栏+侧边栏+右侧内容区+右上角导航条等模块。
<template> <div class="vu__chatbox"> <template v-if="!route?.meta?.isNewWin"> <div class="vu__container flexbox flex-alignc flex-justifyc"> <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> </div> </template>
vite.js+tauri2.0实现无边框圆角阴影拖拽窗体
项目采用 decorations: false 无边框模式。结合 transparent: true 和 shadow: false 实现自定义圆角阴影窗口。
.vu__chatbox {height: calc(100vh); padding: 5px; overflow: hidden;} .vu__layout { background-color: #f5f5f5; overflow: hidden; height: 100%; width: 100%; position: relative; z-index: 100; border-radius: 8px; box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.15),0 1px 5px -1px rgba(0, 0, 0, 0.1),0 2px 5px rgba(0, 0, 0, 0.1); }
- tauri2.0自定义系统最小化/最大化/关闭
<script setup> /** * tauri2.0自定义系统最大化/最小化/关闭 * by andy Q:282310962 */ import { ref } from 'vue' import { getCurrentWindow } from '@tauri-apps/api/window' import { listen } from '@tauri-apps/api/event' import { exit } from '@tauri-apps/plugin-process' import { authState } from '@/pinia/modules/auth' import { isTrue } from '@/utils' import { winSet } from '@/windows/actions' const authstate = authState() const props = defineProps({ color: String, // 窗口是否可最小化 minimizable: {type: [Boolean, String], default: true}, // 窗口是否可最大化 maximizable: {type: [Boolean, String], default: true}, // 窗口是否可关闭 closable: {type: [Boolean, String], default: true}, // 层级 zIndex: {type: [Number, String], default: 2024}, // 关闭前回调,会暂停实例关闭 function(done),done用于关闭 beforeClose: Function }) const hasMaximized = ref(false) const isResizable = ref(true) const isMaximizable = ref(true) // 用户是否可以手动调整窗口大小 getCurrentWindow().isResizable().then(res => { isResizable.value = res }) // 窗口是否可以最大化 getCurrentWindow().isMaximizable().then(res => { isMaximizable.value = res }) // 初始监听窗口是否最大化 getCurrentWindow().isMaximized().then(res => { hasMaximized.value = res }) // 实时监听窗口是否最大化 listen('tauri://resize', async() => { hasMaximized.value = await getCurrentWindow().isMaximized() }) // 最小化 const handleWinMin = async() => { // winSet('minimize') await getCurrentWindow().minimize() } // 最大化/还原 const handleWinToggle = async() => { // winSet('max2min') await getCurrentWindow().toggleMaximize() } // 关闭 const handleClose = async() => { const isMajor = getCurrentWindow().label.indexOf('main') > -1 if(isMajor) { let el = layer({ type: 'android', content: '是否最小化到托盘,不退出程序?', layerStyle: 'background: #f9f9f9; border-radius: 8px;', closable: false, resize: false, btns: [ { text: '最小化托盘', style: 'color: #646cff', click: () => { layer.close(el) // winSet('hide') await getCurrentWindow().hide() } }, { text: '退出程序', style: 'color: #fa5151', click: async() => { authstate.logout() await exit() } } ] }) }else { // winSet('close') await getCurrentWindow().close() } } </script> <template> <div class="ev__winbtns vu__drag" :style="{'z-index': zIndex}"> <div class="ev__winbtns-actions vu__undrag" :style="{'color': color}"> <a v-if="isTrue(minimizable)" class="wbtn min" title="最小化" @click="handleWinMin"><i class="wicon elec-icon elec-icon-min"></i></a> <a v-if="isTrue(maximizable) && isResizable && isMaximizable" class="wbtn toggle" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinToggle"> <i class="wicon elec-icon iconfont" :class="hasMaximized ? 've-icon-shrink' : 've-icon-arrowsalt'"></i> </a> <a v-if="isTrue(closable)" class="wbtn close" title="关闭" @click="handleClose"><i class="wicon elec-icon elec-icon-quit"></i></a> </div> </div> </template>
tauri设置无边框窗口后,拖拽功能直接在需要拖拽的元素设置 data-tauri-drag-region 属性,另外tauri还支持css设置拖拽功能。
// 拖拽 .vu__drag {-webkit-app-region: drag;} // 取消拖拽 .vu__undrag {-webkit-app-region: no-drag;}
tauri2+vue3自定义托盘闪烁|托盘右键菜单
src-tauri/src目录新建一个tray.rs托盘图标文件。
/** * 自定义托盘图标 */ use tauri::{ tray::{MouseButton, TrayIconBuilder, TrayIconEvent}, Emitter, Manager, Runtime }; pub fn tray_create<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> { let _ = TrayIconBuilder::with_id("tray") .tooltip("TAURI-WINCHAT") .icon(app.default_window_icon().unwrap().clone()) .on_tray_icon_event(|tray, event| match event { TrayIconEvent::Click { id: _, position, rect: _, button, button_state: _, } => match button { MouseButton::Left {} => { let windows = tray.app_handle().webview_windows(); for (key, value) in windows { println!("点击左键: {}", key); if key == "main-login" || key == "main" { value.show().unwrap(); value.unminimize().unwrap(); value.set_focus().unwrap(); } } } MouseButton::Right {} => { println!("点击右键"); tray.app_handle().emit("tray_contextmenu", position).unwrap(); } _ => {} }, TrayIconEvent::Enter { id: _, position, rect: _, } => { println!("鼠标滑过托盘"); tray.app_handle().emit("tray_mouseenter", position).unwrap(); } TrayIconEvent::Leave { id: _, position, rect: _, } => { println!("鼠标离开托盘"); tray.app_handle().emit("tray_mouseleave", position).unwrap(); } _ => {} }) .build(app); Ok(()) }
托盘闪烁提醒和右键菜单文件
export default async function TrayContextMenu() { console.log('create tray contextmenu...') const authstate = authState() // 右键菜单宽度 let menuW = 150 // 右键菜单高度 let menuH = authstate.authorization ? 300 : 48 let webview = new WebviewWindow('win-traymenu', { url: '/tray/contextmenu', title: '托盘右键菜单', width: menuW, height: menuH, x: window.screen.width, y: window.screen.height, skipTaskbar: true, transparent: true, shadow: false, decorations: false, center: false, resizable: false, alwaysOnTop: true, focus: true, visible: false }) await webview.listen('tauri://window-created', async() => { const win = await WebviewWindow.getByLabel('win-traymenu') win.hide() }) await webview.listen('tauri://blur', async() => { const win = await WebviewWindow.getByLabel('win-traymenu') win.hide() }) await webview.listen('tauri://error', async(error) => { console.log('traymenu error!', error) }) // 监听托盘右键菜单事件 listen('tray_contextmenu', async(event) => { console.log('tray_contextmenu: ', event) let position = event.payload const win = await WebviewWindow.getByLabel('win-traymenu') if(!win) return win.setAlwaysOnTop(true) win.setFocus() win.setPosition(new LogicalPosition(position.x - 5, position.y - menuH + 5)) win.show() }) }
<template> <div class="vu__traymenu" @click="handleTrayMenu"> <template v-if="authstate.authorization"> <a class="menu"><img src="/static/tray-online.png" />我在线上</a> <a class="menu"><img src="/static/tray-busy.png" />忙碌</a> <a class="menu"><img src="/static/tray-nodisturb.png" />请勿打扰</a> <a class="menu"><img src="/static/tray-hide.png" />隐身</a> <a class="menu"><img src="/static/tray-offline.png" />离线</a> <a class="menu" @click="winTrayFlash(false)">关闭头像闪动</a> <a class="menu" @click="handleSetting">设置</a> <a class="menu" @click="handleAbout">关于</a> <a class="menu" @click="handleMainWin"><el-icon size="12"><Monitor /></el-icon>打开主面板</a> </template> <a class="menu" @click="handleLogout"><el-icon size="12" color="red"><SwitchButton /></el-icon>退出</a> </div> </template>
综上就是Tauri2.0+Vue3实战开发桌面端聊天项目的一些知识分享,希望对大家有些帮助~
最后附上两个最新electron+vue3实例项目
https://www.cnblogs.com/xiaoyan2017/p/18396212
https://www.cnblogs.com/xiaoyan2017/p/18366451
没有回复内容