基于Tauri2+Vue3搭建桌面端程序|tauri2+vite5多窗口|消息提醒|托盤閃爍
基于tauri2+vite5+vue3封裝多窗口實(shí)踐|自定義消息提醒|托盤右鍵菜單及圖標(biāo)閃爍
這段時(shí)間一直在搗鼓最新版Tauri2.x整合Vite5搭建桌面端多開窗體應(yīng)用實(shí)踐。tauri2.0相較于1.0版本api有了比較多的更改,而且tauri2支持創(chuàng)建android/ios應(yīng)用。至于具體的api變更,大家可以去官網(wǎng)查閱文檔資料。

https://v2.tauri.app/start/migrate/from-tauri-1/

Tauri2.8+Vue3聊天系統(tǒng)|vite7+tauri2+element-plus客戶端仿微信聊天程序
Tauri2.0-Vue3OS桌面端os平臺(tái)|tauri2+vite6+arco電腦版OS管理系統(tǒng)
Tauri2-Vite7Admin客戶端管理后臺(tái)|tauri2.9+vue3+element-plus后臺(tái)系統(tǒng)
版本信息
"@tauri-apps/api": ">=2.0.0-rc.0", "@tauri-apps/cli": ">=2.0.0-rc.0", "vue": "^3.3.4", "vite": "^5.3.1"
創(chuàng)建tauri2+vue3項(xiàng)目模板
官網(wǎng)提供了多種方式創(chuàng)建tauri2+vue3項(xiàng)目。

// 創(chuàng)建項(xiàng)目模板 yarn create tauri-app --rc // 進(jìn)入項(xiàng)目目錄 cd tauri-app // 安裝依賴 yarn // 運(yùn)行項(xiàng)目 yarn tauri dev
內(nèi)置了多種熱門前端框架模板可供選擇。


// 運(yùn)行到桌面端 yarn tauri dev // 初始化android yarn tauri android init // 運(yùn)行到android yarn tauri android dev

至此一個(gè)簡(jiǎn)單的tauri2+vue3初始化項(xiàng)目模板就搭建好了。
tauri2封裝多窗口管理
通過封裝一個(gè)tauri多窗口類,只需傳入配置參數(shù),即可快速創(chuàng)建一個(gè)新窗體,簡(jiǎn)化調(diào)用方式。

createWin({ label: 'manage', title: '管理頁面', url: '/manage', width: 960, height: 750, center: false, x: 320, y: 500, resizable: false, alwaysOnTop: true, })

/** * @desc Tauri2多窗口封裝管理 * @author: Andy QQ:282310962 * @time 2024.9 */ import { getAllWindows, getCurrentWindow } from '@tauri-apps/api/window' import { WebviewWindow, getAllWebviewWindows, getCurrentWebviewWindow} from '@tauri-apps/api/webviewWindow' import { relaunch, exit } from '@tauri-apps/plugin-process' import { emit, listen } from '@tauri-apps/api/event' import { setWin } from './actions' const appWindow = getCurrentWindow() // 創(chuàng)建窗口參數(shù)配置 export const windowConfig = { label: null, // 窗口唯一label title: '', // 窗口標(biāo)題 url: '', // 路由地址url width: 1000, // 窗口寬度 height: 640, // 窗口高度 minWidth: null, // 窗口最小寬度 minHeight: null, // 窗口最小高度 x: null, // 窗口相對(duì)于屏幕左側(cè)坐標(biāo) y: null, // 窗口相對(duì)于屏幕頂端坐標(biāo) center: true, // 窗口居中顯示 resizable: true, // 是否支持縮放 maximized: false, // 最大化窗口 decorations: false, // 窗口是否裝飾邊框及導(dǎo)航條 alwaysOnTop: false, // 置頂窗口 dragDropEnabled: false, // 禁止系統(tǒng)拖放 visible: false, // 隱藏窗口 // ... } class Windows { constructor() { // 主窗口 this.mainWin = null } // 創(chuàng)建新窗口 async createWin(options) { console.log('-=-=-=-=-=開始創(chuàng)建窗口') const args = Object.assign({}, windowConfig, options) // 判斷窗口是否存在 const existWin = await this.getWin(args.label) if(existWin) { console.log('窗口已存在>>', existWin) // ... } // 創(chuàng)建窗口對(duì)象 const win = new WebviewWindow(args.label, args) // 窗口創(chuàng)建完畢/失敗 win.once('tauri://created', async() => { console.log('tauri://created') // 是否主窗口 if(args.label.indexOf('main') > -1) { // ... } // 是否最大化 if(args.maximized && args.resizable) { console.log('is-maximized') await win.maximize() } }) win.once('tauri://error', async(error) => { console.log('window create error!', error) }) } // 獲取窗口 async getWin(label) { return await WebviewWindow.getByLabel(label) } // 獲取全部窗口 async getAllWin() { // return getAll() return await getAllWindows() } // 開啟主進(jìn)程監(jiān)聽事件 async listen() { console.log('——+——+——+——+——+開始監(jiān)聽窗口') // 創(chuàng)建新窗體 await listen('win-create', (event) => { console.log(event) this.createWin(event.payload) }) // 顯示窗體 await listen('win-show', async(event) => { if(appWindow.label.indexOf('main') == -1) return await appWindow.show() await appWindow.unminimize() await appWindow.setFocus() }) // 隱藏窗體 await listen('win-hide', async(event) => { if(appWindow.label.indexOf('main') == -1) return await appWindow.hide() }) // 關(guān)閉窗體 await listen('win-close', async(event) => { await appWindow.close() }) // ... } } export default Windows

actions.js封裝一些調(diào)用方法。
import { emit } from '@tauri-apps/api/event'
/**
* @desc 創(chuàng)建新窗口
* @param args {object} {label: 'new', url: '/new', width: 500, height: 300, ...}
*/
export async function createWin(args) {
await emit('win-create', args)
}
// ...
/**
* @desc 登錄窗口
*/
export async function loginWin() {
await createWin({
label: 'main_login',
title: '登錄',
url: '/login',
width: 400,
height: 320,
resizable: false,
alwaysOnTop: true
})
}
export async function mainWin() {
await createWin({
label: 'main',
title: 'TAURI-WINDOWMANAGER',
url: '/',
width: 800,
height: 600,
minWidth: 500,
minHeight: 360,
})
}
export async function aboutWindow() {
await createWin({
label: 'about',
title: '關(guān)于',
url: '/about',
width: 450,
height: 360,
})
}
最新原創(chuàng)重磅新作Tauri2+Vue3仿微信客戶端聊天同步到我的原創(chuàng)作品集。

tauri2創(chuàng)建系統(tǒng)托盤圖標(biāo)|托盤閃爍消息提醒|托盤右鍵菜單
tauri2創(chuàng)建系統(tǒng)托盤圖標(biāo),實(shí)現(xiàn)類似QQ消息提醒,自定義托盤右鍵菜單。


在src-tauri/src目錄下,新建一個(gè)tray.rs托盤文件。

use tauri::{ tray::{MouseButton, TrayIconBuilder, TrayIconEvent}, Emitter, Manager, Runtime }; use std::thread::{sleep}; use std::time::Duration; pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> { let _ = TrayIconBuilder::with_id("tray") .tooltip("tauri") .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 {} => { // ... } MouseButton::Right {} => { tray.app_handle().emit("tray_contextmenu", position).unwrap(); } _ => {} }, TrayIconEvent::Enter { id: _, position, rect: _, } => { tray.app_handle().emit("tray_mouseenter", position).unwrap(); } TrayIconEvent::Leave { id: _, position, rect: _, } => { // sleep(Duration::from_millis(500)); tray.app_handle().emit("tray_mouseleave", position).unwrap(); } _ => {} }) .build(app); Ok(()) }
在lib.rs中引入托盤配置。
// ... mod tray; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() // ... .setup(|app| { #[cfg(all(desktop))] { let handle = app.handle(); tray::create_tray(handle)?; } Ok(()) }) .invoke_handler(tauri::generate_handler![greet]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
- 托盤消息提醒


新建一個(gè)msg新窗口,通過獲取鼠標(biāo)滑過托盤圖標(biāo)的position坐標(biāo)給到msg窗口x,y參數(shù)
import { WebviewWindow } from '@tauri-apps/api/webviewWindow'
import { emit, listen } from '@tauri-apps/api/event'
import { LogicalPosition } from '@tauri-apps/api/window'
export let messageBoxWindowWidth = 280
export let messageBoxWindowHeight = 100
export default async function CreateMsgBox() {
console.log('start create msgbox...')
let webview = new WebviewWindow("msgbox", {
url: "/msg",
title: "消息通知",
width: messageBoxWindowWidth,
height: messageBoxWindowHeight,
skipTaskbar: true,
decorations: false,
center: false,
resizable: false,
alwaysOnTop: true,
focus: true,
x: window.screen.width + 50,
y: window.screen.height + 50,
visible: false
})
// 托盤消息事件
await webview.listen('tauri://window-created', async () => {
console.log('msgbox create')
})
await webview.listen('tauri://blur', async () => {
console.log('msgbox blur')
const win = await WebviewWindow.getByLabel('msgbox')
await win.hide()
})
await webview.listen('tauri://error', async(error) => {
console.log('msgbox error!', error)
})
// 監(jiān)聽托盤事件
let trayEnterListen = listen('tray_mouseenter', async (event) => {
// console.log(event)
const win = await WebviewWindow.getByLabel('msgbox')
if(!win) return
let position = event.payload
if(win) {
await win.setAlwaysOnTop(true)
await win.setFocus()
await win.setPosition(new LogicalPosition(position.x - messageBoxWindowWidth / 2, window.screen.availHeight - messageBoxWindowHeight))
await win.show()
}
})
let trayLeaveListen = listen('tray_mouseleave', async (event) => {
console.log(event)
const win = await WebviewWindow.getByLabel('msgbox')
await win.hide()
})
}
封裝設(shè)置托盤圖標(biāo)閃爍 flashTray(true) 和取消閃爍 flashTray(false)
<script setup>
// ...
const flashTimer = ref(false)
const flashTray = async(bool) => {
let flag = true
if(bool) {
TrayIcon.getById('tray').then(async(res) => {
clearInterval(flashTimer.value)
flashTimer.value = setInterval(() => {
if(flag) {
res.setIcon(null)
}else {
// 支持把自定義圖標(biāo)放在默認(rèn)icons文件夾,通過如下方式設(shè)置圖標(biāo)
// res.setIcon('icons/msg.png')
// 支持把自定義圖標(biāo)放在自定義文件夾tray,需要配置tauri.conf.json參數(shù) "bundle": {"resources": ["tray"]}
res.setIcon('tray/msg.png')
}
flag = !flag
}, 500)
})
}else {
clearInterval(flashTimer.value)
let tray = await TrayIcon.getById("tray")
tray.setIcon('icons/icon.png')
}
}
</script>

托盤圖標(biāo)也支持放在自定義文件夾。

比如:托盤圖標(biāo)放在自定義文件夾tray,則需要配置tauri.conf.json文件resources字段。
"bundle": { ... "resources": [ "tray" ] },
- 托盤右鍵菜單


其實(shí)右鍵菜單窗口和消息提醒窗口原理差不多。
import { ref } from 'vue'
import { WebviewWindow } from '@tauri-apps/api/webviewWindow'
import { emit, listen } from '@tauri-apps/api/event'
import { PhysicalPosition, LogicalPosition } from '@tauri-apps/api/window'
import { TrayIcon } from '@tauri-apps/api/tray'
import { invoke } from '@tauri-apps/api/core'
export let menuBoxWindowWidth = 150
export let menuBoxWindowHeight = JSON.parse(localStorage.getItem('logged')) ? 320 : 45
export default async function CreateTraymenu() {
console.log('start create traymenu...')
let webview = new WebviewWindow("traymenu", {
url: "/menu",
title: "消息通知",
width: menuBoxWindowWidth,
height: menuBoxWindowHeight,
skipTaskbar: true,
decorations: false,
center: false,
resizable: false,
alwaysOnTop: true,
focus: true,
x: window.screen.width + 50,
y: window.screen.height + 50,
visible: false
})
// 托盤消息事件
await webview.listen('tauri://window-created', async () => {
console.log('traymenu create')
})
await webview.listen('tauri://blur', async () => {
console.log('traymenu blur')
const win = await WebviewWindow.getByLabel('traymenu')
await win.hide()
})
await webview.listen('tauri://error', async(error) => {
console.log('traymenu error!', error)
})
// 監(jiān)聽托盤事件
let trayEnterListen = listen('tray_contextmenu', async (event) => {
console.log(event)
const win = await WebviewWindow.getByLabel('traymenu')
if(!win) return
let position = event.payload
if(win) {
await win.setAlwaysOnTop(true)
await win.setFocus()
await win.setPosition(new LogicalPosition(position.x, position.y - menuBoxWindowHeight))
await win.show()
}
})
}
<!--托盤右鍵菜單--> <script setup> import { ref } from 'vue' import { WebviewWindow } from "@tauri-apps/api/webviewWindow" import { TrayIcon } from '@tauri-apps/api/tray' import { invoke } from '@tauri-apps/api/core' const logged = JSON.parse(localStorage.getItem('logged')) const handleMainShow = async () => { const traywin = await WebviewWindow.getByLabel('traymenu') await traywin.hide() const homewin = await WebviewWindow.getByLabel('main') await homewin.show() await homewin.unminimize() await homewin.setFocus() } const flashTimer = ref(false) const flashTray = async(bool) => { let flag = true if(bool) { TrayIcon.getById('tray').then(async(res) => { clearInterval(flashTimer.value) flashTimer.value = setInterval(() => { if(flag) { res.setIcon(null) }else { // res.setIcon(defaultIcon) // 支持把自定義圖標(biāo)放在默認(rèn)icons文件夾,通過如下方式設(shè)置圖標(biāo) // res.setIcon('icons/msg.png') // 支持把自定義圖標(biāo)放在自定義文件夾tray,需要配置tauri.conf.json參數(shù) "bundle": {"resources": ["tray"]} res.setIcon('tray/msg.png') } flag = !flag }, 500) }) }else { clearInterval(flashTimer.value) let tray = await TrayIcon.getById("tray") tray.setIcon('icons/icon.png') } } </script> <template> <div v-if="logged" class="traymenu"> <p class="item">?? 我在線上</p> <p class="item">?? 隱身</p> <p class="item">?? 離開</p> <p class="item">?? 忙碌</p> <p class="item">關(guān)閉所有聲音</p> <p class="item" @click="flashTray(true)">開啟圖標(biāo)閃爍</p> <p class="item" @click="flashTray(false)">關(guān)閉圖標(biāo)閃爍</p> <p class="item" @click="handleMainShow">?? 打開主面板</p> <p class="item">?? 退出</p> </div> <div v-else class="traymenu"> <p class="item">?? 退出</p> </div> </template>
綜上就是tauri2+vue3開發(fā)多窗口實(shí)踐,自定義托盤圖標(biāo)消息提醒,右鍵菜單的一些簡(jiǎn)單分享,效果還是比較粗糙,主要是為了實(shí)現(xiàn)功能思路,希望以上分享對(duì)大家有所幫助哈!
Uniapp-DeepSeek跨三端AI助手|uniapp+vue3+deepseek-v3流式ai聊天模板
Electron35-DeepSeek桌面端AI系統(tǒng)|vue3.5+electron+arco客戶端ai模板
uniapp+vue3聊天室|uni-app+vite4+uv-ui跨端仿微信app聊天語音/朋友圈
uniapp+vue3酒店預(yù)訂|vite5+uniapp預(yù)約訂房系統(tǒng)模板(h5+小程序+App端)
分享兩篇最新Tauri v2.0+Vite5+ElementPlus實(shí)戰(zhàn)項(xiàng)目案例,感興趣的可以去看看。
http://www.rzrgm.cn/xiaoyan2017/p/18437155
http://www.rzrgm.cn/xiaoyan2017/p/18467237
Ending,附上幾個(gè)重磅Electron桌面端原創(chuàng)實(shí)例項(xiàng)目
Electron38-Vue3OS客戶端OS系統(tǒng)|vite7+electron38+arco桌面os后臺(tái)管理
Electron38-Wechat電腦端聊天|vite7+electron38仿微信桌面端聊天系統(tǒng)
electron38-admin桌面端后臺(tái)|Electron38+Vue3+ElementPlus管理系統(tǒng)


浙公網(wǎng)安備 33010602011771號(hào)