一、Tauri 2 的插件体系

Tauri 2 把很多系统能力(对话框、剪贴板、通知、托盘、窗口管理)做成了官方插件。插件的使用分三步:npm 装前端包 → Cargo 装 Rust 依赖 → capabilities 配权限。

二、文件对话框插件

1. 安装:

1
npm install @tauri-apps/plugin-dialog

Rust 端(src-tauri/Cargo.toml):

1
2
[dependencies]
tauri-plugin-dialog = "2"

注册插件(lib.rs):

1
2
3
4
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.invoke_handler(tauri::generate_handler![...])
.run(tauri::generate_context!())

2. 配置权限(src-tauri/capabilities/default.json):

1
2
3
4
5
6
7
8
{
"identifier": "default",
"windows": ["main"],
"permissions": [
"core:default",
"dialog:allow-open"
]
}

3. 前端调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { open, save } from '@tauri-apps/plugin-dialog'

// 选择文件
const file = await open({
multiple: false,
directory: false,
filters: [{ name: '图片', extensions: ['png', 'jpg'] }]
})
// file 为 null 表示用户取消了

// 选择目录
const dir = await open({ directory: true })

// 保存对话框
const path = await save({ defaultPath: 'untitled.txt' })

三、系统托盘插件

托盘是桌面应用”最小化到托盘 + 右键菜单”的标配。

1. 安装:

1
npm install @tauri-apps/plugin-tray-icon

Cargo.toml:

1
2
[dependencies]
tauri-plugin-tray-icon = "2"

2. 创建托盘(lib.rs):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
use tauri::{
menu::{Menu, MenuItem},
tray::{TrayIconBuilder, TrayIconEvent},
Manager,
};

pub fn run() {
tauri::Builder::default()
.setup(|app| {
// 托盘菜单
let show = MenuItem::with_id(app, "show", "显示主窗口", true, None::<&str>)?;
let quit = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&show, &quit])?;

// 创建托盘图标(图标放 src-tauri/icons/)
let _tray = TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.menu(&menu)
.show_menu_on_left_click(false)
.on_menu_event(|app, event| match event.id.as_ref() {
"show" => {
if let Some(win) = app.get_webview_window("main") {
let _ = win.show();
let _ = win.set_focus();
}
}
"quit" => app.exit(0),
_ => {}
})
.on_tray_icon_event(|tray, event| {
if let TrayIconEvent::DoubleClick { .. } = event {
// 双击托盘也显示窗口
let app = tray.app_handle();
if let Some(win) = app.get_webview_window("main") {
let _ = win.show();
let _ = win.set_focus();
}
}
})
.build(app)?;

Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

3. 前端配合”关闭时隐藏到托盘”:

1
2
3
4
5
6
7
8
9
10
import { getCurrentWindow } from '@tauri-apps/api/window'

const appWindow = getCurrentWindow()

// 监听前端事件:关闭按钮时隐藏而不是退出
// 在窗口的 before-close 事件里拦截
appWindow.onCloseRequested(async (event) => {
// 这里可以判断是否允许真正关闭(如弹确认)
// 需要阻止默认关闭时:event.preventDefault()
})

配合 Rust 端在 tauri.conf.json 里配置窗口关闭行为,可以实现”点关闭最小化到托盘”。

四、通知插件

1
npm install @tauri-apps/plugin-notification
1
2
[dependencies]
tauri-plugin-notification = "2"

权限 + 使用:

1
2
3
{
"permissions": ["core:default", "notification:default"]
}
1
2
3
4
5
6
7
8
9
import { isPermissionGranted, requestPermission, sendNotification } from '@tauri-apps/plugin-notification'

let granted = await isPermissionGranted()
if (!granted) {
granted = await requestPermission()
}
if (granted) {
sendNotification({ title: 'Tauri 通知', body: '这是一条系统通知' })
}

五、权限配置的排查思路

Tauri 2 前端调用报 not allowed,几乎都是权限问题。排查顺序:

  1. 插件有没有在 lib.rs 注册(.plugin(...));
  2. capabilities/default.json 的 permissions 有没有对应的 xxx:default 或 xxx:allow-xxx;
  3. 权限标识写没写错(插件文档里都有 Permissions 一节);
  4. 改完配置要重启 npm run tauri dev(capabilities 变更不会热加载)。

常见坑

  1. 托盘图标不显示:图标格式或路径问题,确认 default_window_icon() 有值,或手动指定 icon;
  2. 关闭窗口进程还在但界面没了:需要 Rust 端配合 win.close() 拦截逻辑,只隐藏不销毁;
  3. macOS 通知权限:首次调用会弹系统授权,用户拒绝后要去系统设置里手动开;
  4. window.show() 不生效:多窗口场景确认窗口 label 是 main。

总结

Tauri 插件的套路高度一致:安装 → 注册 → 配权限 → 前端调用。先把 dialog(文件选择)、tray-icon(托盘)、notification(通知)这三个高频插件跑通,桌面应用的”系统级体验”就基本齐了。记得任何新能力的第一排查点永远是 capabilities 权限。