通知系统 #
一、通知概述 #
1.1 通知类型 #
| 类型 | 说明 | 使用场景 |
|---|---|---|
| 系统通知 | 操作系统原生通知 | 重要提醒 |
| 应用内通知 | 应用内部弹窗 | 即时反馈 |
| 徽章通知 | 图标角标数字 | 未读消息 |
1.2 安装插件 #
bash
pnpm add @tauri-apps/plugin-notification
rust
// src-tauri/src/lib.rs
tauri::Builder::default()
.plugin(tauri_plugin_notification::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
1.3 权限配置 #
json
// src-tauri/capabilities/default.json
{
"permissions": [
"notification:default",
"notification:allow-is-permission-granted",
"notification:allow-request-permission",
"notification:allow-notify"
]
}
二、发送通知 #
2.1 基本通知 #
typescript
import { sendNotification } from '@tauri-apps/plugin-notification';
async function showNotification() {
await sendNotification({
title: '通知标题',
body: '这是通知内容',
});
}
2.2 带图标的通知 #
typescript
import { sendNotification } from '@tauri-apps/plugin-notification';
async function showNotificationWithIcon() {
await sendNotification({
title: '新消息',
body: '您有一条新消息',
icon: 'icons/message.png',
});
}
2.3 通知选项 #
typescript
import { sendNotification, Options } from '@tauri-apps/plugin-notification';
const options: Options = {
title: '下载完成',
body: '文件已成功下载',
icon: 'icons/download.png',
sound: 'default',
};
await sendNotification(options);
三、通知权限 #
3.1 检查权限 #
typescript
import { isPermissionGranted } from '@tauri-apps/plugin-notification';
async function checkPermission() {
const granted = await isPermissionGranted();
console.log('Notification permission:', granted);
return granted;
}
3.2 请求权限 #
typescript
import { requestPermission, isPermissionGranted } from '@tauri-apps/plugin-notification';
async function ensurePermission() {
let granted = await isPermissionGranted();
if (!granted) {
const permission = await requestPermission();
granted = permission === 'granted';
}
return granted;
}
3.3 权限处理示例 #
typescript
import {
isPermissionGranted,
requestPermission,
sendNotification,
} from '@tauri-apps/plugin-notification';
async function notify(title: string, body: string) {
let granted = await isPermissionGranted();
if (!granted) {
const permission = await requestPermission();
granted = permission === 'granted';
}
if (granted) {
await sendNotification({ title, body });
} else {
console.warn('Notification permission not granted');
}
}
四、通知管理 #
4.1 创建通知对象 #
typescript
import { Notification } from '@tauri-apps/plugin-notification';
async function createNotification() {
const notification = await Notification.create({
title: '任务完成',
body: '您的任务已完成',
});
// 显示通知
await notification.show();
}
4.2 通知 ID #
typescript
import { Notification } from '@tauri-apps/plugin-notification';
const notification = await Notification.create({
id: 'task-complete',
title: '任务完成',
body: '您的任务已完成',
});
await notification.show();
4.3 取消通知 #
typescript
import { Notification } from '@tauri-apps/plugin-notification';
// 通过 ID 取消
await Notification.cancel('task-complete');
// 取消所有通知
await Notification.cancelAll();
五、通知事件 #
5.1 监听通知点击 #
typescript
import { onAction } from '@tauri-apps/plugin-notification';
const unlisten = await onAction((notification) => {
console.log('Notification clicked:', notification);
// 处理点击事件
});
// 取消监听
unlisten();
5.2 通知关闭事件 #
typescript
import { onNotificationClosed } from '@tauri-apps/plugin-notification';
await onNotificationClosed((notification) => {
console.log('Notification closed:', notification);
});
六、通知封装 #
6.1 通知管理器 #
typescript
// utils/notificationManager.ts
import {
isPermissionGranted,
requestPermission,
sendNotification,
Notification,
} from '@tauri-apps/plugin-notification';
export class NotificationManager {
private static instance: NotificationManager;
private permissionGranted: boolean = false;
private constructor() {}
static getInstance(): NotificationManager {
if (!NotificationManager.instance) {
NotificationManager.instance = new NotificationManager();
}
return NotificationManager.instance;
}
async init(): Promise<void> {
this.permissionGranted = await isPermissionGranted();
if (!this.permissionGranted) {
const permission = await requestPermission();
this.permissionGranted = permission === 'granted';
}
}
async show(title: string, body: string, icon?: string): Promise<boolean> {
if (!this.permissionGranted) {
await this.init();
}
if (!this.permissionGranted) {
console.warn('Notification permission not granted');
return false;
}
await sendNotification({ title, body, icon });
return true;
}
async showWithId(id: string, title: string, body: string): Promise<void> {
if (!this.permissionGranted) {
await this.init();
}
const notification = await Notification.create({
id,
title,
body,
});
await notification.show();
}
async cancel(id: string): Promise<void> {
await Notification.cancel(id);
}
async cancelAll(): Promise<void> {
await Notification.cancelAll();
}
hasPermission(): boolean {
return this.permissionGranted;
}
}
export const notificationManager = NotificationManager.getInstance();
6.2 使用通知管理器 #
typescript
import { notificationManager } from './utils/notificationManager';
// 初始化
await notificationManager.init();
// 发送通知
await notificationManager.show('提示', '操作已完成');
// 发送带 ID 的通知
await notificationManager.showWithId('download-1', '下载', '文件下载中...');
// 取消通知
await notificationManager.cancel('download-1');
七、应用内通知 #
7.1 Toast 组件 #
tsx
import { useState, useEffect } from 'react';
interface ToastProps {
message: string;
type: 'info' | 'success' | 'error' | 'warning';
duration?: number;
onClose: () => void;
}
export function Toast({ message, type, duration = 3000, onClose }: ToastProps) {
useEffect(() => {
const timer = setTimeout(onClose, duration);
return () => clearTimeout(timer);
}, [duration, onClose]);
return (
<div className={`toast toast-${type}`}>
<span>{message}</span>
<button onClick={onClose}>×</button>
</div>
);
}
7.2 Toast 管理器 #
typescript
// hooks/useToast.ts
import { useState, useCallback } from 'react';
interface Toast {
id: string;
message: string;
type: 'info' | 'success' | 'error' | 'warning';
}
export function useToast() {
const [toasts, setToasts] = useState<Toast[]>([]);
const addToast = useCallback((message: string, type: Toast['type'] = 'info') => {
const id = crypto.randomUUID();
setToasts((prev) => [...prev, { id, message, type }]);
}, []);
const removeToast = useCallback((id: string) => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, []);
const info = useCallback((message: string) => addToast(message, 'info'), [addToast]);
const success = useCallback((message: string) => addToast(message, 'success'), [addToast]);
const error = useCallback((message: string) => addToast(message, 'error'), [addToast]);
const warning = useCallback((message: string) => addToast(message, 'warning'), [addToast]);
return { toasts, addToast, removeToast, info, success, error, warning };
}
7.3 使用 Toast #
tsx
import { useToast } from '../hooks/useToast';
import { Toast } from '../components/Toast';
function App() {
const { toasts, removeToast, success, error } = useToast();
const handleSave = async () => {
try {
await saveData();
success('保存成功');
} catch (err) {
error('保存失败');
}
};
return (
<div>
<button onClick={handleSave}>保存</button>
<div className="toast-container">
{toasts.map((toast) => (
<Toast
key={toast.id}
message={toast.message}
type={toast.type}
onClose={() => removeToast(toast.id)}
/>
))}
</div>
</div>
);
}
八、徽章通知 #
8.1 设置徽章 #
typescript
// macOS 和 Windows 支持
import { invoke } from '@tauri-apps/api/core';
async function setBadge(count: number) {
await invoke('set_badge_count', { count });
}
8.2 Rust 实现 #
rust
#[tauri::command]
fn set_badge_count(app: tauri::AppHandle, count: u32) -> Result<(), String> {
#[cfg(target_os = "macos")]
{
app.set_badge_count(count as i32)
.map_err(|e| e.to_string())?;
}
Ok(())
}
九、最佳实践 #
9.1 通知策略 #
typescript
// 根据重要性选择通知方式
async function notifyUser(message: string, important: boolean = false) {
if (important) {
// 重要消息使用系统通知
await notificationManager.show('重要通知', message);
} else {
// 普通消息使用应用内通知
toast.info(message);
}
}
9.2 通知频率控制 #
typescript
class RateLimitedNotifier {
private lastNotification = 0;
private minInterval = 5000; // 5秒
async notify(title: string, body: string): Promise<void> {
const now = Date.now();
if (now - this.lastNotification < this.minInterval) {
console.log('Notification rate limited');
return;
}
this.lastNotification = now;
await notificationManager.show(title, body);
}
}
十、总结 #
10.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 发送通知 | sendNotification |
| 权限管理 | 检查和请求权限 |
| 通知事件 | 点击和关闭事件 |
| 应用内通知 | Toast 组件 |
| 徽章通知 | 角标数字 |
10.2 下一步 #
现在你已经掌握了通知系统,接下来让我们学习 安全架构,了解 Tauri 的安全模型!
最后更新:2026-03-28