首页 > 其他分享 >tauri2.x+vue3实践篇|封装多窗口|tauri2.0自定义托盘闪烁消息提示+右键菜单

tauri2.x+vue3实践篇|封装多窗口|tauri2.0自定义托盘闪烁消息提示+右键菜单

时间:2024-09-17 09:19:52浏览次数:9  
标签:await 自定义 win 多窗口 tauri 右键 async tray event

最近一直在捣鼓 Tauri2.0 跨平台框架,之前也有分享几篇 tauri1.x 实例项目。相较于1.0,tauri2.x框架api有了比较多的变更,而且支持创建android/ios移动端应用。

在这里插入图片描述

实现类似QQ托盘闪烁消息提醒及右键菜单。

在这里插入图片描述

框架信息

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

在这里插入图片描述
https://v2.tauri.app/

初始化搭建tauri2+vue3项目

在这里插入图片描述

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

提供了多种热门前端框架模板,这里选择vue框架。

在这里插入图片描述
创建成功后,会有如下初始化/运行提示。

在这里插入图片描述

// 运行到客户端
yarn tauri dev
// 初始化android
yarn tauri android init
// 运行到android
yarn tauri android dev

在这里插入图片描述
到这里一个tauri2+vue3项目模板就初始化完毕了。

Tauri2封装多开窗口|窗口多开管理

在这里插入图片描述

createWin({
    label: 'index',
    title: '首页',
    url: '/index',
    width: 850,
    height: 600,
    minWidth: 500,
    minHeight: 400,
    center: true,
    resizable: true,
    alwaysOnTop: true,
})

在这里插入图片描述

/**
 * @desc    tauri2封装多窗口管理类
 * @author: andy  Q:282310962
 * @date    2024/9
 */

import { getAllWindows, getCurrentWindow } from '@tauri-apps/api/window'
import { WebviewWindow, getAllWebviewWindows, getCurrentWebviewWindow} from '@tauri-apps/api/webviewWindow'
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 {
    // 创建新窗口
    async createWin(options) {
        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 aboutWin() {
    await createWin({
        label: 'about',
        title: '关于',
        url: '/about',
        width: 450,
        height: 360,
    })
}

tauri2.0实现QQ托盘消息闪烁提醒|右键菜单

在这里插入图片描述

新建一个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");
}
  • 托盘消息提示

在这里插入图片描述
在这里插入图片描述

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() {
    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) => {
        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()
    })
}

设置一个定时器用于实现闪烁效果。

<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>

托盘图标资源文件默认在icons目录下,支持放在自定义文件夹。
在这里插入图片描述

比如:将托盘图标放在自定义文件夹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 { 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() {
    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) => {
        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)
                        // 支持把自定义图标放在默认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">

标签:await,自定义,win,多窗口,tauri,右键,async,tray,event
From: https://blog.csdn.net/yanxinyun1990/article/details/142310404

相关文章

  • 自定义类型:结构体 1
    结构体的声明和初始化1.结构体的声明结构体是用户自定义的数据类型,它能将不同类型的数据组成一个单一的复合类型。定义结构体类型使用struct关键字,后跟结构体名称和大括号内的成员列表。如structstu{ charname[20]; intage;charsex[5];};//分号不能丢2.变......
  • urllib自定义opener对象设置代理IP
    urllib.request.urlopen()源代码——urlopen()在干什么返回opener.open(url,data,timeout)方法的结果 _opener=None#_opener被赋值为Nonedefurlopen(url,data=None,timeout=socket._GLOBAL_DEFAULT_TIMEOUT,*,cafile=None,capath=None,cadefault=......
  • 鸿蒙开发入门day18-自定义扩展
    (创作不易,感谢有你,你的支持,就是我前行的最大动力,如果看完对你有帮助,还请三连支持一波哇ヾ(@^∇^@)ノ)目录自定义扩展能力概述AttributeModifierAttributeUpdaterAttributeModifier概述接口定义行为规格属性设置与修改设置多态样式、事件AttributeUpdater概述接口定......
  • Python编程之旅:定义自定义异常的艺术
    引言在实际开发过程中,我们经常会遇到各种各样的错误情况,如数据类型不符、资源访问失败等。这时候,合理地使用异常处理机制就显得尤为重要了。Python内置了许多异常类,但有时候它们并不能完全满足我们的需求。这时,就需要我们自己动手定义一些特定场景下的异常类型了。定义自定义异常......
  • 自定义字体加载
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>CustomFontLoading</ti......
  • 「ComfyUI」生图修图神器,自定义调节颜色光暗,更生动更强对比度生图技巧分享!
    今天再给小伙伴们分享一个简单又实用生图神器插件,可以调整整个图像的光暗变化以及颜色变化。原理的话,我们也简单来说下,我们在使用VAE将图像编码为潜在噪声时,VAE解码的值通常在一定范围内,使得最终的潜在噪声只有较小的值范围,而作者做的就是编写了一个函数扩大了这个值的范围......
  • dede怎么添加自定义属性
    在DedeCMS中添加自定义属性可以通过修改数据库表来实现。以下是具体的步骤:登录数据库管理工具:登录到你的数据库管理工具,如phpMyAdmin。修改dede_archives表:寻找dede_archives表,并打开其结构。找到flag字段,这是一个枚举类型字段,用于存储文档的一些标志。编辑flag字段,......
  • 优化批处理流程:自定义BatchProcessorUtils的设计与应用
    优化批处理流程:自定义BatchProcessorUtils的设计与应用| 原创作者/编辑:凯哥Java                    | 分类:个人小工具类在我们开发过程中,处理大量的数据集是一项常见的任务。特别是在数据库操作、文件处理或者任何需要对大量数据进行分......
  • C# 使用NPOI 导出文件到Excel.支持分页及自定义排序
    导出帮助类usingNPOI.HSSF.UserModel;usingNPOI.OpenXmlFormats.Spreadsheet;usingNPOI.OpenXmlFormats.Vml;usingNPOI.SS.UserModel;usingNPOI.SS.Util;usingSystem;usingSystem.Collections.Generic;usingSystem.Drawing;usingSystem.IO;usingSystem.Text;......
  • jvm 自定义dns
    对于jvm进行自定义dns可以解决不少问题(比如特定系统需要一个额外的域名改写,但是并不希望进行全局修改)对于java1.4-8我们是可以直接进行dns配置的(系统属性就可以了),之后的版本就不行了,具体参考dnsjava的说明实际配置参考配置-Dsun.net.spi.nameservice.provider.1=dns,sun-D......