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

news/2024/9/19 16:31:13

基于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,            // 窗口唯一labeltitle: '',              // 窗口标题url: '',                // 路由地址urlwidth: 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) returnawait appWindow.show()await appWindow.unminimize()await appWindow.setFocus()})// 隐藏窗体await listen('win-hide', async(event) => {if(appWindow.label.indexOf('main') == -1) returnawait 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 = 100export 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) returnlet position = event.payloadif(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 = trueif(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 : 45export 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) returnlet position = event.payloadif(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 = trueif(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开发多窗口实践,自定义托盘图标消息提醒,右键菜单的一些简单分享,功能还是比较粗糙,主要是为了实现功能思路,希望以上分享对大家有所帮助哈!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/61150.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

[CTFshow] 命令执行 29-77,118-124

web29 简单过滤 error_reporting(0); if(isset($_GET[c])){$c = $_GET[c];if(!preg_match("/flag/i", $c)){eval($c);}}else{highlight_file(__FILE__); }?c=system(tac fla*); web30 error_reporting(0); if(isset($_GET[c])){$c = $_GET[c];if(!preg_match("…

ComfyUI 基础教程(五) —— 应用 IP-Adapter 实现图像风格迁移

来吧,理解 IP-Adapter。中秋假期,又可以玩玩 AI 了。前面介绍了 ComfyUI 的 Lora 模型以及 ControlNet,本文介绍另一个非常重要且使用的节点,IP-Adapter。 一、 IP-Adapter 概念 1.1 IPAdapter 的介绍 IP-Adapter 的是腾讯 ailab 实验室发布的一个 Stable Diffusion 的适配…

创建一个虚拟机

需要一个iso文件(镜像)

数据结构——树状数组

前言: 树状数组,利用二进制乱七八糟的东西的数据结构。 trick: 修改,查询: ​ 区间修改和单点修改分开来说,区间修改相当于树状数组维护的是一个差分数组,区间修改对应的单点查询,单点修改对应的区间查询,前者是维护差分数组,后者维护的是原本的数组,这次不过多赘述…

白云龙期货投资-第四讲

趋势线波浪理论总结: 1.一般行情完成一次阶段性的上涨或者下跌都会通过三波来完成; 2.这三波上涨和下跌的时间空间,经常同等 3.可以利用波浪理论以上两个特性来判断和预测,还会有几次的上涨或者下跌行情,以及每次大概运行的时间及空间 三种常用实用突破法 1.早盘30mins突破…

中秋 -2024/9/16

今天是中秋假期的第二天,已经过了2/3了,怎么俺滴中秋这么快就没了 今天学习了SQL语句种的查询聚合函数进行查询和Java种的集合 TreeSet,HashSet,LinkedHashSet DQL-聚合函数介绍:将一列数据作为一个整体,进行纵向计算 常见聚合函数count - 统计数量 max - 最大值 min - 最小值 …

李尚杰的第一次作业

这次作业属于哪个课程 https://edu.cnblogs.com/campus/zjlg/rjjc这个作业的目标 熟悉博客的建立,向老师/助教介绍自己并阐述对课程的期待姓名-学号 李尚杰-2022329301146一、个人简介 (一)基本介绍我叫李尚杰,来自22自动化1班,浙江杭州人。我爱好摄影、旅游、看电影、健身…