新功能: - 实现完整的图像上传组件 (ImageUploader) - 支持拖拽上传、文件选择、预览和验证 - 文件大小和格式限制 - 实时预览和文件管理 - 实现AI图像分析结果展示 (OutfitAnalysisResult) - 实时状态更新和进度跟踪 - 分析结果详情查看 - 自动刷新机制 - 添加文件保存功能 (save_outfit_image 命令) - 更新OutfitMatch页面为现代化标签页设计 技术改进: - 修复服装搭配相关模型的编译错误 - 添加缺失的trait实现 (Eq, Hash) - 修复生命周期参数问题 - 完善错误处理和用户反馈 UI/UX优化: - 现代化的标签页设计 - 响应式布局和优雅动画 - 统一的设计语言和交互体验 - 完善的加载状态和错误处理
238 lines
8.3 KiB
Rust
238 lines
8.3 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use chrono::{DateTime, Utc};
|
|
use super::outfit_analysis::ColorHSV;
|
|
|
|
/// 服装类别枚举
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum OutfitCategory {
|
|
Top, // 上装 (T恤、衬衫、毛衣等)
|
|
Bottom, // 下装 (裤子、裙子等)
|
|
Dress, // 连衣裙
|
|
Outerwear, // 外套
|
|
Footwear, // 鞋类
|
|
Accessory, // 配饰
|
|
Other, // 其他
|
|
}
|
|
|
|
impl OutfitCategory {
|
|
pub fn from_string(category: &str) -> Self {
|
|
match category.to_lowercase().as_str() {
|
|
"上装" | "t恤" | "衬衫" | "毛衣" | "背心" | "top" => Self::Top,
|
|
"下装" | "裤子" | "牛仔裤" | "短裤" | "裙子" | "bottom" => Self::Bottom,
|
|
"连衣裙" | "长裙" | "dress" => Self::Dress,
|
|
"外套" | "夹克" | "大衣" | "outerwear" => Self::Outerwear,
|
|
"鞋子" | "运动鞋" | "高跟鞋" | "footwear" => Self::Footwear,
|
|
"配饰" | "帽子" | "包包" | "首饰" | "accessory" => Self::Accessory,
|
|
_ => Self::Other,
|
|
}
|
|
}
|
|
|
|
pub fn to_chinese(&self) -> &'static str {
|
|
match self {
|
|
Self::Top => "上装",
|
|
Self::Bottom => "下装",
|
|
Self::Dress => "连衣裙",
|
|
Self::Outerwear => "外套",
|
|
Self::Footwear => "鞋类",
|
|
Self::Accessory => "配饰",
|
|
Self::Other => "其他",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 服装风格枚举
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum OutfitStyle {
|
|
Casual, // 休闲
|
|
Formal, // 正式
|
|
Business, // 商务
|
|
Sporty, // 运动
|
|
Vintage, // 复古
|
|
Bohemian, // 波西米亚
|
|
Minimalist, // 极简
|
|
Streetwear, // 街头
|
|
Elegant, // 优雅
|
|
Trendy, // 时尚
|
|
Other(String), // 其他自定义风格
|
|
}
|
|
|
|
impl OutfitStyle {
|
|
pub fn from_string(style: &str) -> Self {
|
|
match style.to_lowercase().as_str() {
|
|
"casual" | "休闲" => Self::Casual,
|
|
"formal" | "正式" => Self::Formal,
|
|
"business" | "商务" => Self::Business,
|
|
"sporty" | "运动" => Self::Sporty,
|
|
"vintage" | "复古" => Self::Vintage,
|
|
"bohemian" | "波西米亚" => Self::Bohemian,
|
|
"minimalist" | "极简" => Self::Minimalist,
|
|
"streetwear" | "街头" => Self::Streetwear,
|
|
"elegant" | "优雅" => Self::Elegant,
|
|
"trendy" | "时尚" => Self::Trendy,
|
|
_ => Self::Other(style.to_string()),
|
|
}
|
|
}
|
|
|
|
pub fn to_string(&self) -> String {
|
|
match self {
|
|
Self::Casual => "休闲".to_string(),
|
|
Self::Formal => "正式".to_string(),
|
|
Self::Business => "商务".to_string(),
|
|
Self::Sporty => "运动".to_string(),
|
|
Self::Vintage => "复古".to_string(),
|
|
Self::Bohemian => "波西米亚".to_string(),
|
|
Self::Minimalist => "极简".to_string(),
|
|
Self::Streetwear => "街头".to_string(),
|
|
Self::Elegant => "优雅".to_string(),
|
|
Self::Trendy => "时尚".to_string(),
|
|
Self::Other(s) => s.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 服装尺寸信息
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OutfitSize {
|
|
pub size_label: String, // 尺码标签 (如: "M", "L", "XL")
|
|
pub measurements: Option<SizeMeasurements>, // 具体尺寸
|
|
}
|
|
|
|
/// 具体尺寸测量
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SizeMeasurements {
|
|
pub chest: Option<f64>, // 胸围 (cm)
|
|
pub waist: Option<f64>, // 腰围 (cm)
|
|
pub hip: Option<f64>, // 臀围 (cm)
|
|
pub length: Option<f64>, // 长度 (cm)
|
|
pub sleeve: Option<f64>, // 袖长 (cm)
|
|
}
|
|
|
|
/// 服装材质信息
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OutfitMaterial {
|
|
pub fabric_type: String, // 面料类型 (如: "棉", "聚酯纤维")
|
|
pub fabric_composition: Vec<FabricComponent>, // 面料成分
|
|
pub care_instructions: Vec<String>, // 护理说明
|
|
}
|
|
|
|
/// 面料成分
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct FabricComponent {
|
|
pub material: String, // 材料名称
|
|
pub percentage: f64, // 百分比
|
|
}
|
|
|
|
/// 服装单品实体
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OutfitItem {
|
|
pub id: String,
|
|
pub project_id: String,
|
|
pub analysis_id: Option<String>, // 关联的分析记录ID
|
|
pub name: String, // 服装名称
|
|
pub category: OutfitCategory, // 服装类别
|
|
pub brand: Option<String>, // 品牌
|
|
pub model: Option<String>, // 型号
|
|
pub color_primary: ColorHSV, // 主色调
|
|
pub color_secondary: Option<ColorHSV>, // 次要色调
|
|
pub styles: Vec<OutfitStyle>, // 风格标签
|
|
pub design_elements: Vec<String>, // 设计元素 (如: "条纹", "印花", "刺绣")
|
|
pub size: Option<OutfitSize>, // 尺寸信息
|
|
pub material: Option<OutfitMaterial>, // 材质信息
|
|
pub price: Option<f64>, // 价格
|
|
pub purchase_date: Option<DateTime<Utc>>, // 购买日期
|
|
pub image_urls: Vec<String>, // 图片URL列表
|
|
pub tags: Vec<String>, // 自定义标签
|
|
pub notes: Option<String>, // 备注
|
|
pub created_at: DateTime<Utc>,
|
|
pub updated_at: DateTime<Utc>,
|
|
}
|
|
|
|
/// 创建服装单品请求
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CreateOutfitItemRequest {
|
|
pub project_id: String,
|
|
pub analysis_id: Option<String>,
|
|
pub name: String,
|
|
pub category: OutfitCategory,
|
|
pub brand: Option<String>,
|
|
pub model: Option<String>,
|
|
pub color_primary: ColorHSV,
|
|
pub color_secondary: Option<ColorHSV>,
|
|
pub styles: Vec<OutfitStyle>,
|
|
pub design_elements: Vec<String>,
|
|
pub size: Option<OutfitSize>,
|
|
pub material: Option<OutfitMaterial>,
|
|
pub price: Option<f64>,
|
|
pub purchase_date: Option<DateTime<Utc>>,
|
|
pub image_urls: Vec<String>,
|
|
pub tags: Vec<String>,
|
|
pub notes: Option<String>,
|
|
}
|
|
|
|
/// 更新服装单品请求
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct UpdateOutfitItemRequest {
|
|
pub name: Option<String>,
|
|
pub category: Option<OutfitCategory>,
|
|
pub brand: Option<String>,
|
|
pub model: Option<String>,
|
|
pub color_primary: Option<ColorHSV>,
|
|
pub color_secondary: Option<ColorHSV>,
|
|
pub styles: Option<Vec<OutfitStyle>>,
|
|
pub design_elements: Option<Vec<String>>,
|
|
pub size: Option<OutfitSize>,
|
|
pub material: Option<OutfitMaterial>,
|
|
pub price: Option<f64>,
|
|
pub purchase_date: Option<DateTime<Utc>>,
|
|
pub image_urls: Option<Vec<String>>,
|
|
pub tags: Option<Vec<String>>,
|
|
pub notes: Option<String>,
|
|
}
|
|
|
|
/// 服装单品查询选项
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OutfitItemQueryOptions {
|
|
pub project_id: Option<String>,
|
|
pub category: Option<OutfitCategory>,
|
|
pub styles: Option<Vec<OutfitStyle>>,
|
|
pub color_similarity_threshold: Option<f64>, // 颜色相似度阈值
|
|
pub target_color: Option<ColorHSV>, // 目标颜色
|
|
pub brand: Option<String>,
|
|
pub tags: Option<Vec<String>>,
|
|
pub limit: Option<u32>,
|
|
pub offset: Option<u32>,
|
|
}
|
|
|
|
/// 服装单品统计信息
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct OutfitItemStats {
|
|
pub total_count: u32,
|
|
pub category_counts: std::collections::HashMap<String, u32>,
|
|
pub style_counts: std::collections::HashMap<String, u32>,
|
|
pub brand_counts: std::collections::HashMap<String, u32>,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_outfit_category_conversion() {
|
|
assert_eq!(OutfitCategory::from_string("T恤"), OutfitCategory::Top);
|
|
assert_eq!(OutfitCategory::from_string("牛仔裤"), OutfitCategory::Bottom);
|
|
assert_eq!(OutfitCategory::from_string("连衣裙"), OutfitCategory::Dress);
|
|
}
|
|
|
|
#[test]
|
|
fn test_outfit_style_conversion() {
|
|
assert_eq!(OutfitStyle::from_string("休闲"), OutfitStyle::Casual);
|
|
assert_eq!(OutfitStyle::from_string("正式"), OutfitStyle::Formal);
|
|
|
|
let custom_style = OutfitStyle::from_string("自定义风格");
|
|
match custom_style {
|
|
OutfitStyle::Other(s) => assert_eq!(s, "自定义风格"),
|
|
_ => panic!("Expected Other variant"),
|
|
}
|
|
}
|
|
}
|