innovationtauri2.0+vue3+pinai2Imitation QQ/WeChat Client Chat Exe ProgramTauriWinChat。
tauri2-vue3-winchatself-researchvite5+tauri2.0+vue3 setup+element-plusCross-platform imitation QQ|WeChat desktop chat software. NewEncapsulating tauri2 multi-open window management, custom rounded shadow formsThe RealizationChat, Contacts, Favorites, My, Circle of Friends/Small Videoand other modules.
technology stack
- Encoder: VScode
- Technical framework: tauri2.0+vite^5.4.7+vue^3.5.8+vue-router^4.4.5
- Status management: pinia^2.2.2
- Local storage: pinia-plugin-persistedstate^4.0.2
- UI component library: element-plus^2.8.3
- Rich text editor: @vueup/vue-quill^1.2.0
- Style preprocessing: sass^1.79.3
- Small video slider component: swiper^11.1.14
Project framework structure
vue3-taurichat desktop chat project uses the latest cross-platform frameworkstauri2.0 conformvite5 Build a project template.
Tauri 2.0-Vue3chat chat program has been synchronized to my original portfolio, you can check it out if you need~
/item/detail/1107133011
Integrate tauri 2.0 to create multi-window | Customize tray blinking context menu
I've previously shared an article dedicated to tauri2 combined with building desktop projects, creating multiple windows, customizing tray blinking and context menus. If you are interested, you can take a look.
/xiaoyan2017/p/18416811
tauri2-vue3chat implements QQ-like login/main window switching, supports theme wallpaper, top window, custom maximize/minimize/close buttons, and the chat module supports image/video preview in a new window, and dragging and dropping images to the chat area.
main entrance
/** * :: Main entry document */ import { createApp } from 'vue' import './' import App from './' // Introducing component libraries import VEPlus from 've-plus' import 've-plus/dist/' import ElementPlus from 'element-plus' import 'element-plus/dist/' // Introduction of routing/state management import Router from './router' import Pinia from './pinia' createApp(App) .use(VEPlus) .use(ElementPlus) .use(Router) .use(Pinia) .mount('#app')
Tauri2.0-Vue3chat Layout Template
The project as a whole is divided intoMenu bar + sidebar + right content area + top right navigation barand other modules.
<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" @> <!-- menu bar--> <slot v-if="!route?.meta?.hideMenuBar" name="menubar"> <MenuBar /> </slot> <!-- a side-bar (in software)--> <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> <!-- main content area--> <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="" /> </keep-alive> </router-view> </div> </div> </div> </div> </template> <template v-else> <WinLayout /> </template> </div> </template>
+tauri2.0 implement borderless rounded shadow drag and drop form
The project usesdecorations: false Bezel-less mode. Combined withtransparent: true cap (a poem)shadow: false Implement custom rounded corner shadow windows.
.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); }
- tauri 2.0 customize system minimize/maximize/close
<script setup> 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, // Whether the window can be minimized minimizable: {type: [Boolean, String], default: true}, // Whether the window can be maximized or not maximizable: {type: [Boolean, String], default: true}, // Whether the window can be closed or not closable: {type: [Boolean, String], default: true}, // level zIndex: {type: [Number, String], default: 2024}, // Pre-close callback that will pause the instance closing function(done), done is used to close the beforeClose: Function }) const hasMaximized = ref(false) const isResizable = ref(true) const isMaximizable = ref(true) // Whether the user can manually resize the window getCurrentWindow().isResizable().then(res => { = res }) // Whether the window can be maximized getCurrentWindow().isMaximizable().then(res => { = res }) // Initially listens to whether the window is maximized or not getCurrentWindow().isMaximized().then(res => { = res }) // Listen to the maximization of the window in real time listen('tauri://resize', async() => { = await getCurrentWindow().isMaximized() }) // minimize (computing) const handleWinMin = async() => { // winSet('minimize') await getCurrentWindow().minimize() } // Maximize/Restore const handleWinToggle = async() => { // winSet('max2min') await getCurrentWindow().toggleMaximize() } // cloture const handleClose = async() => { const isMajor = getCurrentWindow().('main') > -1 if(isMajor) { let el = layer({ type: 'android', content: 'Does it minimize to the tray without exiting the program?', layerStyle: 'background: #f9f9f9; border-radius: 8px;', closable: false, resize: false, btns: [ { text: 'Minimize Tray', style: 'color: #646cff', click: () => { (el) // winSet('hide') await getCurrentWindow().hide() } }, { text: 'Exit the program', style: 'color: #fa5151', click: async() => { () 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="Minimize." @click="handleWinMin"><i class="wicon elec-icon elec-icon-min"></i></a> <a v-if="isTrue(maximizable) && isResizable && isMaximizable" class="wbtn toggle" :title="hasMaximized ? 'Restore Down' : 'Maximized'" @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="Close." @click="handleClose"><i class="wicon elec-icon elec-icon-quit"></i></a> </div> </div> </template>
After tauri sets up a borderless window, the drag-and-drop function is set up directly on the element that needs to be dragged and droppeddata-tauri-drag-region attribute, in addition tauri also supports css settings drag and drop functionality.
// Drag and drop .vu__drag{-webkit-app-region: drag;} // Undo Drag and Drop .vu__undrag{-webkit-app-region: no-drag;}
tauri2+vue3 customize tray blinking | tray context menu
The src-tauri/src directory creates a new tray icon file.
/** * :: Customized tray icons */ 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!("left click: {}", key); if key == "main-login" || key == "main" { ().unwrap(); ().unwrap(); value.set_focus().unwrap(); } } } MouseButton::Right {} => { println!("Right click."); tray.app_handle().emit("tray_contextmenu", position).unwrap(); } _ => {} }, TrayIconEvent::Enter { id: _, position, rect: _, } => { println!("Mouse over tray."); tray.app_handle().emit("tray_mouseenter", position).unwrap(); } TrayIconEvent::Leave { id: _, position, rect: _, } => { println!("Mouse off tray."); tray.app_handle().emit("tray_mouseleave", position).unwrap(); } _ => {} }) .build(app); Ok(()) }
Tray blink alert and right-click menu files
export default async function TrayContextMenu() { ('create tray contextmenu...') const authstate = authState() // Right-click menu width let menuW = 150 // Right-click menu height let menuH = ? 300 : 48 let webview = new WebviewWindow('win-traymenu', { url: '/tray/contextmenu', title: 'Tray right-click menu', width: menuW, height: menuH, x: , y: , skipTaskbar: true, transparent: true, shadow: false, decorations: false, center: false, resizable: false, alwaysOnTop: true, focus: true, visible: false }) await ('tauri://window-created', async() => { const win = await ('win-traymenu') () }) await ('tauri://blur', async() => { const win = await ('win-traymenu') () }) await ('tauri://error', async(error) => { ('traymenu error!', error) }) // Listening to tray context menu events listen('tray_contextmenu', async(event) => { ('tray_contextmenu: ', event) let position = const win = await ('win-traymenu') if(!win) return (true) () (new LogicalPosition( - 5, - menuH + 5)) () }) }
<template> <div class="vu__traymenu" @click="handleTrayMenu"> <template v-if=""> <a class="menu"><img src="/static/" />I'm on the line.</a> <a class="menu"><img src="/static/" />bustling</a> <a class="menu"><img src="/static/" />please do not disturb</a> <a class="menu"><img src="/static/" />invisible (person or online status)</a> <a class="menu"><img src="/static/" />offline (computing)</a> <a class="menu" @click="winTrayFlash(false)">Turn off avatar flashing</a> <a class="menu" @click="handleSetting">set up</a> <a class="menu" @click="handleAbout">with respect to</a> <a class="menu" @click="handleMainWin"><el-icon size="12"><Monitor /></el-icon>Open the main panel</a> </template> <a class="menu" @click="handleLogout"><el-icon size="12" color="red"><SwitchButton /></el-icon>abort</a> </div> </template>
In summary, Tauri2.0 + Vue3 Hands-on development desktop chat project to share some knowledge, I hope you have some help ~!
Finally, I've attached the two latest electron+vue3 example projects.
/xiaoyan2017/p/18396212
/xiaoyan2017/p/18366451