diff --git a/apps/desktop/src-tauri/src/data/models/template_matching_result.rs b/apps/desktop/src-tauri/src/data/models/template_matching_result.rs index bd7f82e..6671d88 100644 --- a/apps/desktop/src-tauri/src/data/models/template_matching_result.rs +++ b/apps/desktop/src-tauri/src/data/models/template_matching_result.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, Deserializer}; use chrono::{DateTime, Utc}; /// 模板匹配结果实体模型 @@ -28,7 +28,7 @@ pub struct TemplateMatchingResult { } /// 匹配结果状态 -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, PartialEq)] pub enum MatchingResultStatus { /// 匹配成功 Success, @@ -40,6 +40,27 @@ pub enum MatchingResultStatus { Cancelled, } +/// 自定义反序列化实现,处理空字符串和无效值 +impl<'de> Deserialize<'de> for MatchingResultStatus { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match s.as_str() { + "Success" => Ok(MatchingResultStatus::Success), + "PartialSuccess" => Ok(MatchingResultStatus::PartialSuccess), + "Failed" => Ok(MatchingResultStatus::Failed), + "Cancelled" => Ok(MatchingResultStatus::Cancelled), + "" => { + // 空字符串被视为无效值,返回错误让调用者处理 + Err(serde::de::Error::custom("Empty string is not a valid MatchingResultStatus")) + } + _ => Err(serde::de::Error::unknown_variant(&s, &["Success", "PartialSuccess", "Failed", "Cancelled"])), + } + } +} + impl Default for MatchingResultStatus { fn default() -> Self { Self::Success @@ -294,16 +315,44 @@ pub struct CreateTemplateMatchingResultRequest { pub description: Option, } +/// 自定义反序列化函数,处理空字符串状态 +fn deserialize_optional_status<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let opt = Option::::deserialize(deserializer)?; + match opt { + Some(s) if s.is_empty() => Ok(None), // 空字符串转换为 None + Some(s) => match s.as_str() { + "Success" => Ok(Some(MatchingResultStatus::Success)), + "PartialSuccess" => Ok(Some(MatchingResultStatus::PartialSuccess)), + "Failed" => Ok(Some(MatchingResultStatus::Failed)), + "Cancelled" => Ok(Some(MatchingResultStatus::Cancelled)), + _ => Err(serde::de::Error::unknown_variant(&s, &["Success", "PartialSuccess", "Failed", "Cancelled"])), + }, + None => Ok(None), + } +} + /// 模板匹配结果查询选项 #[derive(Debug, Default, Serialize, Deserialize)] pub struct TemplateMatchingResultQueryOptions { + #[serde(default)] pub project_id: Option, + #[serde(default)] pub template_id: Option, + #[serde(default)] pub binding_id: Option, + #[serde(default, deserialize_with = "deserialize_optional_status")] pub status: Option, + #[serde(default)] pub limit: Option, + #[serde(default)] pub offset: Option, + #[serde(default)] pub search_keyword: Option, + #[serde(default)] pub sort_by: Option, // "created_at", "success_rate", "result_name" + #[serde(default)] pub sort_order: Option, // "asc", "desc" } diff --git a/apps/desktop/src/components/TemplateMatchingResultManager.tsx b/apps/desktop/src/components/TemplateMatchingResultManager.tsx index 957b15f..424c377 100644 --- a/apps/desktop/src/components/TemplateMatchingResultManager.tsx +++ b/apps/desktop/src/components/TemplateMatchingResultManager.tsx @@ -69,7 +69,7 @@ export const TemplateMatchingResultManager: React.FC handleFilterChange({ status: value as MatchingResultStatus })} + onChange={(value) => handleFilterChange({ + status: value === '' ? undefined : value as MatchingResultStatus + })} placeholder="选择状态" />