/// 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#" {title} {body} "#, 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()) }