You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kev/Drawer/AI/Models/SpecialMessages/XyzLoadCardMessage.cs

168 lines
7.0 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using AI.Models.Form;
namespace AI.Models.SpecialMessages
{
/// <summary>
/// 散点文件加载卡片的阶段
/// </summary>
public enum XyzLoadPhase
{
SelectFile = 0, // 初始:等待用户选择文件
FileLoaded = 1, // 文件已加载,显示数据预览 + 列头匹配
Completed = 2, // 列头匹配已确认,摘要已提交
}
/// <summary>
/// 散点文件加载综合卡片:将「打开散点文件」「数据预览」「列头匹配」合并到一张卡片。
/// 整个业务流程由卡片本身驱动,不需要 AI 参与中间步骤;
/// 用户确认列头匹配后,统一生成摘要发给 AI。
/// </summary>
public class XyzLoadCardMessage : ISpecialMessage, INotifyPropertyChanged
{
private XyzLoadPhase _phase = XyzLoadPhase.SelectFile;
private string _filePath = string.Empty;
private string _matchButtonLabel = "确认匹配";
private TableDataMessage? _tablePreview;
private bool _isLoading;
private string _statusMessage = string.Empty;
/// <summary>消息唯一标识符</summary>
public string Id { get; set; } = Guid.NewGuid().ToString();
/// <summary>类型名称(用于序列化路由)</summary>
public string TypeName => "XyzLoadCard";
/// <summary>不需要实时更新</summary>
public bool IsLive => false;
// ── 阶段控制 ──────────────────────────────────────────────────────────
public XyzLoadPhase Phase
{
get => _phase;
set
{
if (SetProperty(ref _phase, value))
{
OnPropertyChanged(nameof(IsFileInputEnabled));
OnPropertyChanged(nameof(ShowLoadButtons));
OnPropertyChanged(nameof(HasSubmittedFile));
OnPropertyChanged(nameof(ShowPreviewSection));
OnPropertyChanged(nameof(ShowMatchSection));
OnPropertyChanged(nameof(ShowMatchButton));
}
}
}
// ── 加载状态 ──────────────────────────────────────────────────────────
/// <summary>是否正在执行加载(禁用按钮,显示 loading 提示)</summary>
public bool IsLoading
{
get => _isLoading;
set
{
if (SetProperty(ref _isLoading, value))
{
OnPropertyChanged(nameof(ShowLoadButtons));
OnPropertyChanged(nameof(IsFileInputEnabled));
}
}
}
/// <summary>状态提示文案(加载中、错误信息等)</summary>
public string StatusMessage
{
get => _statusMessage;
set
{
SetProperty(ref _statusMessage, value ?? string.Empty);
OnPropertyChanged(nameof(HasStatusMessage));
}
}
/// <summary>是否有错误提示</summary>
public bool HasStatusMessage => !string.IsNullOrEmpty(_statusMessage);
// ── 第一步:选择文件 ─────────────────────────────────────────────────
/// <summary>文件路径(双向绑定到输入框)</summary>
public string FilePath
{
get => _filePath;
set => SetProperty(ref _filePath, value ?? string.Empty);
}
/// <summary>文件输入框是否可编辑(仅 SelectFile 且非加载中时)</summary>
public bool IsFileInputEnabled => Phase == XyzLoadPhase.SelectFile && !IsLoading;
/// <summary>是否显示「选择文件」和「加载」按钮SelectFile 且非加载中)</summary>
public bool ShowLoadButtons => Phase == XyzLoadPhase.SelectFile && !IsLoading;
/// <summary>是否显示正在加载提示</summary>
public bool ShowLoadingHint => IsLoading;
/// <summary>文件已提交后显示「已加载」标记</summary>
public bool HasSubmittedFile => Phase != XyzLoadPhase.SelectFile;
// ── 第二步:数据预览 ─────────────────────────────────────────────────
/// <summary>CSV 数据预览(加载后填充)</summary>
public TableDataMessage? TablePreview
{
get => _tablePreview;
set
{
if (SetProperty(ref _tablePreview, value))
{
OnPropertyChanged(nameof(ShowPreviewSection));
}
}
}
/// <summary>是否显示数据预览区</summary>
public bool ShowPreviewSection => _tablePreview != null && Phase != XyzLoadPhase.SelectFile;
// ── 第三步:列头匹配 ─────────────────────────────────────────────────
/// <summary>列头匹配下拉字段(必需列 → 可用列 ComboBox</summary>
public ObservableCollection<FormFieldEntry> ColumnMatchFields { get; } = new();
/// <summary>列头匹配表单定义(保存 title、submitTarget用于构建摘要</summary>
public FormDefinition? ColumnMatchDefinition { get; set; }
/// <summary>确认匹配按钮文案</summary>
public string MatchButtonLabel
{
get => _matchButtonLabel;
set => SetProperty(ref _matchButtonLabel, value);
}
/// <summary>是否显示列头匹配区FileLoaded 阶段且有匹配字段)</summary>
public bool ShowMatchSection => Phase == XyzLoadPhase.FileLoaded;
/// <summary>是否显示「确认匹配」按钮FileLoaded 阶段)</summary>
public bool ShowMatchButton => Phase == XyzLoadPhase.FileLoaded;
// ── INotifyPropertyChanged ───────────────────────────────────────────
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
}