work-order-monitor/src-tauri/src/notification.rs

72 lines
2.3 KiB
Rust

/// Windows Toast 通知模块
/// 通过本地 HTTP 端点桥接按钮点击事件,所有按钮均使用 protocol 激活(兼容非打包应用)
#[cfg(target_os = "windows")]
use windows::Data::Xml::Dom::XmlDocument;
#[cfg(target_os = "windows")]
use windows::UI::Notifications::{ToastNotification, ToastNotificationManager};
#[cfg(target_os = "windows")]
fn xml_escape(s: &str) -> String {
s.replace('&', "&")
.replace('<', "&lt;")
.replace('>', "&gt;")
.replace('"', "&quot;")
.replace('\'', "&apos;")
}
/// 发送带交互按钮的 Toast 通知
///
/// - 点击消息体 → 关闭通知(非打包应用不支持 foreground 激活)
/// - 点击"推迟1小时" → 通过 localhost 回调暂停监测并弹出应用窗口
/// - 点击"去处理" → 直接打开 CRM 网站
#[cfg(target_os = "windows")]
pub fn send_interactive_toast(
title: &str,
body: &str,
port: u16,
app_name: &str,
) -> Result<(), String> {
let toast_xml = format!(
r#"<toast activationType="protocol">
<visual>
<binding template="ToastGeneric">
<text>{title}</text>
<text>{body}</text>
</binding>
</visual>
<actions>
<action content="推迟1小时" arguments="http://127.0.0.1:{port}/postpone" activationType="protocol"/>
<action content="去处理" arguments="http://127.0.0.1:{port}/handle" activationType="protocol"/>
</actions>
</toast>"#,
port = port,
title = xml_escape(title),
body = xml_escape(body),
);
let xml = XmlDocument::new().map_err(|e| e.to_string())?;
xml.LoadXml(&toast_xml.into()).map_err(|e| e.to_string())?;
let toast =
ToastNotification::CreateToastNotification(&xml).map_err(|e| e.to_string())?;
let notifier =
ToastNotificationManager::CreateToastNotifierWithId(&app_name.into())
.map_err(|e| e.to_string())?;
notifier.Show(&toast).map_err(|e| e.to_string())?;
Ok(())
}
#[cfg(not(target_os = "windows"))]
pub fn send_interactive_toast(
_title: &str,
_body: &str,
_port: u16,
_app_name: &str,
) -> Result<(), String> {
Err("Toast 通知仅支持 Windows 平台".to_string())
}