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#

1 month ago
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;
}
}
}