2026-03-10 14:41:13 +08:00
|
|
|
/// 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('<', "<")
|
|
|
|
|
.replace('>', ">")
|
|
|
|
|
.replace('"', """)
|
|
|
|
|
.replace('\'', "'")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 发送带交互按钮的 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"/>
|
2026-03-10 15:20:06 +08:00
|
|
|
<action content="去处理" arguments="http://127.0.0.1:{port}/handle" activationType="protocol"/>
|
2026-03-10 14:41:13 +08:00
|
|
|
</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())
|
|
|
|
|
}
|