Tauri2 vue3 rust js 托盘图标闪烁及未读消息小窗展示

ps: 对于rust语言不熟悉, 国内资料匮乏,所参考不完整,所幸仔细阅读文档后,最终实现了效果

参考: Rust官网 Tauri官网

工程初始化

可以参考往期的Tauri的博客文章《Tauri小而美》

托盘图标闪烁

托盘图标闪烁目前方案为周期性设置托盘图标的显示和隐藏,

参考自:

https://v2.tauri.app/plugin/system-tray/

https://www.cnblogs.com/xiaoyan2017/p/18416811

可以看到Tauri官网的Demo为js语言和rust语言,rust语言使用不熟练,所以采用了js+rust的方式来实现

首先在文件目录中创建tray.rs文件, 完成托盘图标的初始化

按此目录结构选取一张托盘展示图片

然后在tauri.conf.json中添加参数供系统识别图片位置

// tray.rs 全文件
use tauri::{
    Emitter,
    Manager,
    Runtime,
};

// 托盘菜单定义设置
pub fn create_tray<R: tauri::Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
    // 创建托盘图标
    let _tray = tauri::tray::TrayIconBuilder::with_id("tray")  // 托盘图标ID
        // .menu(&menu)
        .tooltip("金投")
        .icon(app.default_window_icon().unwrap().clone())
        .on_menu_event(move |app, event| match event.id.as_ref() {
            "quit" => {
                app.exit(0);
            }
            "show" => {
                if let Some(window) = app.get_webview_window("main") {
                    let _ = window.show();
                }
            }
            "hide" => {
                if let Some(window) = app.get_webview_window("main") {
                    let _ = window.hide();
                }
            }
            _ => {
                println!("menu item {:?} not handled", event.id);
            }
        })
        .on_tray_icon_event(|tray, event| match event {
            tauri::tray::TrayIconEvent::Click {
                id: _,
                position,
                button,
                ..
            } => match button {
                tauri::tray::MouseButton::Left => {
                    println!("Left click");
                    tray.app_handle()
                        .emit("tray_mouseleftclick", position)
                        .unwrap();
                }
                tauri::tray::MouseButton::Right => {
                    println!("Right click");
                    tray.app_handle()
                        .emit("tray_contextmenu", position)
                        .unwrap();
                }
                _ => {}
            },
            tauri::tray::TrayIconEvent::Enter { position, .. } => {
                tray.app_handle().emit("tray_mouseenter", position).unwrap();
            }
            tauri::tray::TrayIconEvent::Leave { position, .. } => {
                tray.app_handle().emit("tray_mouseleave", position).unwrap();
            }
            _ => {}
        })
        .build(app)?;

    Ok(())
}

在vue页面中触发图标周期性显隐

<script setup>
import { ref } from "vue";
import { TrayIcon } from "@tauri-apps/api/tray";

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("tray/msg.png");
        }
        flag = !flag;
      }, 500);
    });
  } else {
    clearInterval(flashTimer.value);
    let tray = await TrayIcon.getById("tray");
    tray.setIcon("icons/icon.png");
  }
};

</script>

<template>
  <main class="container">
    <h1>Welcome to Tauri + Vue</h1>

    <div class="row">
      <a href="https://vitejs.dev" target="_blank">
        <img src="/vite.svg" class="logo vite" alt="Vite logo" />
      </a>
      <a href="https://tauri.app" target="_blank">
        <img src="/tauri.svg" class="logo tauri" alt="Tauri logo" />
      </a>
      <a href="https://vuejs.org/" target="_blank">
        <img src="../assets/vue.svg" class="logo vue" alt="Vue logo" />
      </a>
    </div>
    <p>Click on the Tauri, Vite, and Vue logos to learn more.</p>

    <form class="row" @submit.prevent="greet">
      <input id="greet-input" v-model="name" placeholder="Enter a name..." />
      <button type="submit">Greet</button>
    </form>
    <div @click="flashTray(true)">托盘闪烁</div>

    <div @click="flashTray(false)">关闭托盘闪烁</div>
  </main>
</template>

<style scoped>
.logo.vite:hover {
  filter: drop-shadow(0 0 2em #747bff);
}

.logo.vue:hover {
  filter: drop-shadow(0 0 2em #249b73);
}
</style>
<style>
:root {
  font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
  font-size: 16px;
  line-height: 24px;
  font-weight: 400;

  color: #0f0f0f;
  background-color: #f6f6f6;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
}

.container {
  margin: 0;
  padding-top: 10vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
}

.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: 0.75s;
}

.logo.tauri:hover {
  filter: drop-shadow(0 0 2em #24c8db);
}

.row {
  display: flex;
  justify-content: center;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}

a:hover {
  color: #535bf2;
}

h1 {
  text-align: center;
}

input,
button {
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  color: #0f0f0f;
  background-color: #ffffff;
  transition: border-color 0.25s;
  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
}

button {
  cursor: pointer;
}

button:hover {
  border-color: #396cd8;
}
button:active {
  border-color: #396cd8;
  background-color: #e8e8e8;
}

input,
button {
  outline: none;
}

#greet-input {
  margin-right: 5px;
}

@media (prefers-color-scheme: dark) {
  :root {
    color: #f6f6f6;
    background-color: #2f2f2f;
  }

  a:hover {
    color: #24c8db;
  }

  input,
  button {
    color: #ffffff;
    background-color: #0f0f0f98;
  }
  button:active {
    background-color: #0f0f0f69;
  }
}
</style>

注意以上操作均需要在tauri中设置对应的权限,否则无法生效

ps: 个别的权限未设置,不会有任何报错

在capabilities中添加 权限 webview.json

{
  "identifier": "webview",
  "description": "Capability for the main window",
  "windows": ["*"],
  "permissions": [
    "core:webview:allow-create-webview-window",
    "core:webview:allow-create-webview",
    "core:webview:allow-webview-close",
    "core:webview:allow-create-webview-window"
  ]
}

和权限 window.json

{
	"identifier": "window",
	"description": "Capability for the main window",
	"windows": ["*"],
	"permissions": [
        "core:window:default",
		"core:window:allow-create",
		"core:window:allow-center",
		"core:window:allow-request-user-attention",
		"core:window:allow-set-resizable",
		"core:window:allow-set-maximizable",
		"core:window:allow-set-minimizable",
		"core:window:allow-set-closable",
		"core:window:allow-set-title",
		"core:window:allow-maximize",
		"core:window:allow-unmaximize",
		"core:window:allow-minimize",
		"core:window:allow-unminimize",
		"core:window:allow-show",
		"core:window:allow-hide",
		"core:window:allow-close",
		"core:window:allow-destroy",
		"core:window:allow-set-decorations",
		"core:window:allow-set-always-on-top",
		"core:window:allow-set-content-protected",
		"core:window:allow-set-size",
		"core:window:allow-set-min-size",
		"core:window:allow-set-max-size",
		"core:window:allow-set-position",
		"core:window:allow-set-fullscreen",
		"core:window:allow-set-focus",
		"core:window:allow-set-icon",
		"core:window:allow-set-skip-taskbar",
		"core:window:allow-set-cursor-grab",
		"core:window:allow-set-cursor-visible",
		"core:window:allow-set-cursor-icon",
		"core:window:allow-set-cursor-position",
		"core:window:allow-set-ignore-cursor-events",
		"core:window:allow-start-dragging"
	]
}

以上即可实现Tauri托盘图标闪烁

ps: 参考代码待整理后会放到github