feat(): 重构界面

This commit is contained in:
tsl
2026-03-10 14:41:13 +08:00
parent 71bb171f48
commit da677a033e
25 changed files with 4697 additions and 2766 deletions
+80 -15
View File
@@ -4120,7 +4120,7 @@ dependencies = [
"tao-macros",
"unicode-segmentation",
"url",
"windows",
"windows 0.61.3",
"windows-core 0.61.2",
"windows-version",
"x11-dl",
@@ -4192,7 +4192,7 @@ dependencies = [
"webkit2gtk",
"webview2-com",
"window-vibrancy",
"windows",
"windows 0.61.3",
]
[[package]]
@@ -4210,6 +4210,7 @@ dependencies = [
"tauri-plugin-notification",
"tauri-plugin-opener",
"tauri-plugin-store",
"windows 0.58.0",
]
[[package]]
@@ -4389,7 +4390,7 @@ dependencies = [
"tauri-plugin",
"thiserror 2.0.18",
"url",
"windows",
"windows 0.61.3",
"zbus",
]
@@ -4431,7 +4432,7 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
"windows",
"windows 0.61.3",
]
[[package]]
@@ -4457,7 +4458,7 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
"windows",
"windows 0.61.3",
"wry",
]
@@ -4518,7 +4519,7 @@ checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9"
dependencies = [
"quick-xml 0.37.5",
"thiserror 2.0.18",
"windows",
"windows 0.61.3",
"windows-version",
]
@@ -5321,10 +5322,10 @@ checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a"
dependencies = [
"webview2-com-macros",
"webview2-com-sys",
"windows",
"windows 0.61.3",
"windows-core 0.61.2",
"windows-implement",
"windows-interface",
"windows-implement 0.60.2",
"windows-interface 0.59.3",
]
[[package]]
@@ -5345,7 +5346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
dependencies = [
"thiserror 2.0.18",
"windows",
"windows 0.61.3",
"windows-core 0.61.2",
]
@@ -5395,6 +5396,16 @@ dependencies = [
"windows-version",
]
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core 0.58.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows"
version = "0.61.3"
@@ -5417,14 +5428,27 @@ dependencies = [
"windows-core 0.61.2",
]
[[package]]
name = "windows-core"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement 0.58.0",
"windows-interface 0.58.0",
"windows-result 0.2.0",
"windows-strings 0.1.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-implement 0.60.2",
"windows-interface 0.59.3",
"windows-link 0.1.3",
"windows-result 0.3.4",
"windows-strings 0.4.2",
@@ -5436,8 +5460,8 @@ version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-implement 0.60.2",
"windows-interface 0.59.3",
"windows-link 0.2.1",
"windows-result 0.4.1",
"windows-strings 0.5.1",
@@ -5454,6 +5478,17 @@ dependencies = [
"windows-threading",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
@@ -5465,6 +5500,17 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "windows-interface"
version = "0.59.3"
@@ -5509,6 +5555,15 @@ dependencies = [
"windows-strings 0.5.1",
]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-result"
version = "0.3.4"
@@ -5527,6 +5582,16 @@ dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result 0.2.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
@@ -5964,7 +6029,7 @@ dependencies = [
"webkit2gtk",
"webkit2gtk-sys",
"webview2-com",
"windows",
"windows 0.61.3",
"windows-core 0.61.2",
"windows-version",
"x11-dl",
+6
View File
@@ -29,3 +29,9 @@ serde_json = "1"
log = "0.4"
env_logger = "0.10"
[target.'cfg(windows)'.dependencies]
windows = { version = "0.58", features = [
"Data_Xml_Dom",
"UI_Notifications",
] }
+4
View File
@@ -35,6 +35,10 @@
"core:tray:allow-set-tooltip",
"core:window:allow-close",
"core:window:allow-destroy",
"core:window:allow-show",
"core:window:allow-hide",
"core:window:allow-set-focus",
"core:window:allow-unminimize",
"core:image:default",
"core:image:allow-from-path"
]
+99 -23
View File
@@ -1,23 +1,99 @@
use tauri_plugin_autostart::MacosLauncher;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
env_logger::init();
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_autostart::init(
MacosLauncher::LaunchAgent,
Some(vec![]),
))
.on_window_event(|window, event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
api.prevent_close();
let _ = window.hide();
}
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
use std::io::{Read, Write};
use std::net::TcpListener;
use std::sync::atomic::{AtomicU16, Ordering};
use tauri::{Emitter, Manager};
use tauri_plugin_autostart::MacosLauncher;
mod notification;
const APP_NAME: &str = "工单系统监测";
static CALLBACK_PORT: AtomicU16 = AtomicU16::new(0);
/// 启动本地 HTTP 服务器,用于接收 Toast 通知按钮的回调
fn start_notification_callback_server(app_handle: tauri::AppHandle) {
let listener =
TcpListener::bind("127.0.0.1:0").expect("failed to bind notification callback server");
let port = listener.local_addr().unwrap().port();
CALLBACK_PORT.store(port, Ordering::SeqCst);
log::info!("Notification callback server on port {}", port);
std::thread::spawn(move || {
for stream in listener.incoming().flatten() {
handle_callback_request(&app_handle, stream);
}
});
}
fn handle_callback_request(app_handle: &tauri::AppHandle, mut stream: std::net::TcpStream) {
let mut buf = [0u8; 1024];
let n = stream.read(&mut buf).unwrap_or(0);
let request = String::from_utf8_lossy(&buf[..n]);
let (action, html) = if request.contains("GET /postpone") {
let _ = app_handle.emit("notification-action", "postpone_1h");
if let Some(w) = app_handle.get_webview_window("main") {
let _ = w.unminimize();
let _ = w.show();
let _ = w.set_focus();
}
("postpone", "已推迟1小时,监控将在1小时后恢复")
} else {
("unknown", "未知操作")
};
log::info!("Notification callback: {}", action);
let body = format!(
"<!DOCTYPE html><html><head><meta charset='utf-8'></head>\
<body style='display:flex;justify-content:center;align-items:center;\
height:80vh;font-family:system-ui;color:#333'>\
<div style='text-align:center'><h2>{}</h2>\
<p style='color:#888'>此标签页可以关闭</p></div></body>\
<script>setTimeout(()=>window.close(),1500)</script></html>",
html
);
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
body.len(),
body
);
let _ = stream.write_all(response.as_bytes());
}
#[tauri::command]
fn send_clickable_notification(title: String, body: String) -> Result<(), String> {
let port = CALLBACK_PORT.load(Ordering::SeqCst);
if port == 0 {
return Err("Notification callback server not ready".into());
}
notification::send_interactive_toast(&title, &body, port, APP_NAME)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
env_logger::init();
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_autostart::init(
MacosLauncher::LaunchAgent,
Some(vec![]),
))
.setup(|app| {
start_notification_callback_server(app.handle().clone());
Ok(())
})
.invoke_handler(tauri::generate_handler![send_clickable_notification])
.on_window_event(|window, event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
api.prevent_close();
let _ = window.emit("close-requested", ());
}
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
+71
View File
@@ -0,0 +1,71 @@
/// 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('&', "&amp;")
.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="https://crm.yunvip123.com/index.html" 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())
}
+39 -35
View File
@@ -1,35 +1,39 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "tauri-app",
"version": "0.1.0",
"identifier": "com.administrator.tauri-app",
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "pnpm build",
"frontendDist": "../dist"
},
"app": {
"windows": [
{
"title": "tauri-app",
"width": 800,
"height": 600
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
}
}
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "工单系统监测",
"version": "0.1.1",
"identifier": "com.administrator.tauri-app",
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "pnpm build",
"frontendDist": "../dist"
},
"app": {
"windows": [
{
"title": "工单系统监测",
"width": 800,
"height": 600
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": true,
"targets": ["nsis"],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": [
"icons/32x32.png",
"icons/icon.ico"
]
}
}