基于tauri2+vite5+vue3封装多窗口实践|自定义消息提醒|托盘右键菜单及图标闪烁

这段时间一直在捣鼓最新版Tauri2.x整合Vite5搭建桌面端多开窗体应用实践。tauri2.0相较于1.0版本api有了比较多的更改,而且tauri2支持创建android/ios应用。至于具体的api变更,大家可以去官网查阅文档资料。

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

版本信息

"@tauri-apps/api": ">=2.0.0-rc.0",
"@tauri-apps/cli": ">=2.0.0-rc.0",
"vue": "^3.3.4",
"vite": "^5.3.1"

创建tauri2+vue3项目模板

官网提供了多种方式创建tauri2+vue3项目。

// 创建项目模板
yarn create tauri-app --rc
// 进入项目目录
cd tauri-app
// 安装依赖
yarn
// 运行项目
yarn tauri dev

内置了多种热门前端框架模板可供选择。

// 运行到桌面端
yarn tauri dev
// 初始化android
yarn tauri android init
// 运行到android
yarn tauri android dev

至此一个简单的tauri2+vue3项目模板就搭建好了。

tauri2封装多窗口管理

通过封装一个tauri多窗口类,只需传入配置参数,即可快速创建一个新窗体,简化调用方式。

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() // 创建窗口参数配置
export const windowConfig = {
label: null, // 窗口唯一label
title: '', // 窗口标题
url: '', // 路由地址url
width: 1000, // 窗口宽度
height: 640, // 窗口高度
minWidth: null, // 窗口最小宽度
minHeight: null, // 窗口最小高度
x: null, // 窗口相对于屏幕左侧坐标
y: null, // 窗口相对于屏幕顶端坐标
center: true, // 窗口居中显示
resizable: true, // 是否支持缩放
maximized: false, // 最大化窗口
decorations: false, // 窗口是否装饰边框及导航条
alwaysOnTop: false, // 置顶窗口
dragDropEnabled: false, // 禁止系统拖放
visible: false, // 隐藏窗口 // ...
} class Windows {
constructor() {
// 主窗口
this.mainWin = null
} // 创建新窗口
async createWin(options) {
console.log('-=-=-=-=-=开始创建窗口') const args = Object.assign({}, windowConfig, options) // 判断窗口是否存在
const existWin = await this.getWin(args.label)
if(existWin) {
console.log('窗口已存在>>', existWin)
// ...
}
// 创建窗口对象
const win = new WebviewWindow(args.label, args) // 窗口创建完毕/失败
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()
} // 开启主进程监听事件
async listen() {
console.log('——+——+——+——+——+开始监听窗口') // 创建新窗体
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()
}) // 关闭窗体
await listen('win-close', async(event) => {
await appWindow.close()
}) // ...
}
} export default Windows

actions.js封装一些调用方法。

import { emit } from '@tauri-apps/api/event'

/**
* @desc 创建新窗口
* @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: '关于',
url: '/about',
width: 450,
height: 360,
})
}

tauri2创建系统托盘图标|托盘闪烁消息提醒|托盘右键菜单

tauri2创建系统托盘图标,实现类似QQ消息提醒,自定义托盘右键菜单。

在src-tauri/src目录下,新建一个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");
}
  • 托盘消息提醒

新建一个msg新窗口,通过获取鼠标滑过托盘图标的position坐标给到msg窗口x,y参数

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)
}) // 监听托盘事件
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()
})
}

设置托盘图标闪烁 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 {
// 支持把自定义图标放在默认icons文件夹,通过如下方式设置图标
// res.setIcon('icons/msg.png')
// 支持把自定义图标放在自定义文件夹tray,需要配置tauri.conf.json参数 "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>

或者放在自定义文件夹。

如果放在自定义文件夹tray,则需要配置tauri.conf.json文件resources字段。

"bundle": {
...
"resources": [
"tray"
]
},
  • 托盘右键菜单

其实窗口原理和消息提醒差不多。

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)
}) // 监听托盘事件
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()
}
})
}

Msg/index.vue模板

<!--托盘右键菜单-->
<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)
// 支持把自定义图标放在默认icons文件夹,通过如下方式设置图标
// res.setIcon('icons/msg.png')
// 支持把自定义图标放在自定义文件夹tray,需要配置tauri.conf.json参数 "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">关闭所有声音</p>
<p class="item" @click="flashTray(true)">开启图标闪烁</p>
<p class="item" @click="flashTray(false)">关闭图标闪烁</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开发多窗口实践,自定义托盘图标消息提醒,右键菜单的一些简单分享,功能还是比较粗糙,主要是为了实现功能思路,希望以上分享对大家有所帮助哈!

基于Tauri2+Vue3搭建桌面端程序|tauri2+vite5多窗口|消息提醒|托盘闪烁的更多相关文章

  1. ve-plus:基于 vue3.x 桌面端UI组件库|vue3组件库

    VE-Plus 自研轻量级 vue3.js 桌面pc端UI组件库 经过一个多月的筹划及开发,今天给大家带来一款全新的Vue3桌面端UI组件库VEPlus.新增了35+常用的组件,采用vue3 setu ...

  2. 基于Vue.js PC桌面端弹出框组件|vue自定义弹层组件|vue模态框

    vue.js构建的轻量级PC网页端交互式弹层组件VLayer. 前段时间有分享过一个vue移动端弹窗组件,今天给大家分享一个最近开发的vue pc端弹出层组件. VLayer 一款集Alert.Dia ...

  3. 使用electron-vue搭建桌面应用程序项目

    vue-cli+electron一种新的脚手架(vue-electron)vue-electron主要业务逻辑都放在src下的renderer文件夹内,和之前的vue-cli搭建项目流程没有任何区别 ...

  4. 将现有vue项目基于electron打包成桌面应用程序

    一.前言 项目本来打算采用B/S架构去做的,浏览器网址方式打开还是让用户不方便: 二.使用electron集成桌面应用 本身项目是使用vue-cli开发的,在使用electron之前,需要将本身的项目 ...

  5. 将现有vue项目基于electron打包成桌面应用程序 如何隐藏electron窗体的菜单栏

    一.前言 项目本来打算采用B/S架构去做的,浏览器网址方式打开还是让用户不方便: 二.使用electron集成桌面应用 本身项目是使用vue-cli开发的,在使用electron之前,需要将本身的项目 ...

  6. Tauri-Vue3桌面端聊天室|tauri+vite3仿微信|tauri聊天程序EXE

    基于tauri+vue3.js+vite3跨桌面端仿微信聊天实例TauriVue3Chat. tauri-chat 运用最新tauri+vue3+vite3+element-plus+v3layer等 ...

  7. vue3系列:vue3.0自定义全局弹层V3Layer|vue3.x pc桌面端弹窗组件

    基于Vue3.0开发PC桌面端自定义对话框组件V3Layer. 前两天有分享一个vue3.0移动端弹出层组件,今天分享的是最新开发的vue3.0版pc端弹窗组件. V3Layer 一款使用vue3.0 ...

  8. 浅谈入行Qt桌面端开发程序员-从毕业到上岗(1):当我们说到桌面端开发时,我们在谈论什么?

    谈谈我自己 大家好,我是轩先生,是一个刚入行的Qt桌面端开发程序员.我的本科是双非一本的数学专业,22年毕业,只是部分课程与计算机之间有所交叉,其实在我毕业的时候并没有想过会成为一名程序员,也没有想过 ...

  9. 基于tauri+vue3.x多开窗口|Tauri创建多窗体实践

    最近一种在捣鼓 Tauri 集成 Vue3 技术开发桌面端应用实践,tauri 实现创建多窗口,窗口之间通讯功能. 开始正文之前,先来了解下 tauri 结合 vue3.js 快速创建项目. taur ...

  10. Electron-Vue3-Vadmin后台系统|vite2+electron桌面端权限管理系统

    基于vite2.x+electron12桌面端后台管理系统Vite2ElectronVAdmin. 继上一次分享vite2整合electron搭建后台框架,这次带来的是最新开发的跨桌面中后台权限管理系 ...

随机推荐

  1. 安装PHP拓展

    win环境下: php扩展下载地址:http://pecl.php.net/ 需要知道:  php版本,操作系统位数,线程是否安全.想要知道这3个,在php中输入.如下图所示:phpinfo();di ...

  2. Python爬虫(5-10)-编解码、ajax的get请求、ajax的post请求、URLError/HTTPError、微博的cookie登录、Handler处理器

    五.编解码(Unicode编码) (1)GET请求 所提方法都在urllib.parse.路径下 get请求的quote()方法(适用于只提交一两个参数值) url='http://www.baidu ...

  3. 网络基础 CAS协议学习总结

    架构介绍 系统组件 CAS服务器和客户端构成了CAS系统体系结构的两个物理组件,它们通过各种协议进行通信. CAS服务器 CAS服务器是基于Spring Framework构建的Java servle ...

  4. Linux 文本文件编辑相关命令简介【Linux 常用命令系列二】

    〇.前言 本文介绍了如何通过 vim 命令,对文本文件进行打开.编辑.保存等相关操作,并通过简单的示例演示了常用用法. 一.关于文本文件的操作 1.1 打开,查看(cat).编辑(vim) 打开文本文 ...

  5. 在英特尔 Gaudi 2 上加速蛋白质语言模型 ProtST

    引言 蛋白质语言模型 (Protein Language Models, PLM) 已成为蛋白质结构与功能预测及设计的有力工具.在 2023 年国际机器学习会议 (ICML) 上,MILA 和英特尔实 ...

  6. RBAC权限模型概述

    RBAC即role-based access control,基于角色的访问控制 通过角色来管理用户对系统资源的访问权限.RBAC是一种权限管理模型,核心思想是分离用户与具体权限,通过角色作为中介来实 ...

  7. 【Spring-Security】Re11 Oauth2协议 P2 Redis存储 密码模式令牌

    一.Redis配置 需要的依赖 <dependency> <groupId>org.springframework.boot</groupId> <artif ...

  8. Linux:移动当前目录的前N个文件到目标文件夹下

    #移动当前目录的前N个文件到指定文件夹.其中一点代表的是当前目录.两点代表的是上一级目录.subfoleder1代表的是目标目录. mv `ls | head -N` ./subfolder1/ #这 ...

  9. [学习笔记] 单调队列优化DP - DP

    单调队列优化DP 简单好想的DP优化 真正的教育是把学过的知识忘掉后剩下的东西 -- *** 对于一个转移方程类似于 \(dp[i]=max(min)\{dp[j]+b[j]+a[i]\}\ \ x_ ...

  10. Java IO流的简单使用 通俗易懂 超详细 【内含案例】

    IO流简单使用 InputStream 字节输入流 OutputStream 字节输出流 Reader 字符输入流 Writer 字符输出流 代码示例 输入和输出是相对于程序来说的,读取到程序中叫做输 ...