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


    基于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(app: &tauri::AppHandle) -> 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) 

    复制代码
    复制代码

    或者放在自定义文件夹。

    如果放在自定义文件夹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开发多窗口实践,自定义托盘图标消息提醒,右键菜单的一些简单分享,功能还是比较粗糙,主要是为了实现功能思路,希望以上分享对大家有所帮助哈!

     

  • 相关阅读:
    【idea】查看类或方法中的实现
    NVIDIA 7th SkyHackathon(二)Nemo ASR
    Parameter estimation for text analysis (上)
    【数据结构】查找总结
    【Web前端基础进阶学习】HTML详解(下篇)
    TypeScript使用@来映射文件路径解决找不到模块“@/xxx”或其相应的类型声明的问题
    华为携手去哪儿、九牧等企业,共论鸿蒙生态发展蓝图
    高性能MySQL-创建高性能索引
    枚举类型的使用
    Spring Boot面试必问:自动配置原理
  • 原文地址:https://www.cnblogs.com/xiaoyan2017/p/18416811