205 lines
5.5 KiB
C#
205 lines
5.5 KiB
C#
using OrderSupportTicket.Models.Api;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using WPFCommon.Models;
|
|
|
|
namespace OrderSupportTicket.ViewModels;
|
|
|
|
/// <summary>
|
|
/// 工单留言板中的单条消息。
|
|
/// </summary>
|
|
public sealed class ChatMessageViewModel : BindableBaseEx
|
|
{
|
|
private string _messageId;
|
|
private int _sequenceNo;
|
|
private string _senderType;
|
|
private string _senderDisplayName;
|
|
private string _content;
|
|
private bool _isRecalled;
|
|
private DateTimeOffset _createdAt;
|
|
|
|
public ChatMessageViewModel(TicketMessageDto dto)
|
|
{
|
|
if (dto == null)
|
|
throw new ArgumentNullException(nameof(dto));
|
|
Attachments = new ObservableCollection<ChatAttachmentViewModel>();
|
|
MessageId = dto.MessageId;
|
|
SequenceNo = dto.SequenceNo;
|
|
SenderType = dto.SenderType;
|
|
IsRecalled = dto.Recalled;
|
|
SenderDisplayName = string.IsNullOrWhiteSpace(dto.SenderName)
|
|
? (IsCustomer ? "我" : "客服")
|
|
: dto.SenderName;
|
|
Content = dto.Recalled
|
|
? "该消息已撤回"
|
|
: (dto.Text ?? string.Empty);
|
|
CreatedAt = ParseTimestamp(dto.CreatedAt);
|
|
ReplaceAttachments(dto.Attachments);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 消息唯一 ID。
|
|
/// </summary>
|
|
public string MessageId
|
|
{
|
|
get => _messageId;
|
|
private set => SetValue(ref _messageId, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 工单内连续递增的消息序号。
|
|
/// </summary>
|
|
public int SequenceNo
|
|
{
|
|
get => _sequenceNo;
|
|
private set => SetValue(ref _sequenceNo, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 消息发送方类型。
|
|
/// </summary>
|
|
public string SenderType
|
|
{
|
|
get => _senderType;
|
|
private set
|
|
{
|
|
if (SetValue(ref _senderType, value))
|
|
RaisePropertyChanged(nameof(IsCustomer));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 消息发送方显示名称。
|
|
/// </summary>
|
|
public string SenderDisplayName
|
|
{
|
|
get => _senderDisplayName;
|
|
private set => SetValue(ref _senderDisplayName, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 消息展示内容;撤回消息显示固定提示文本。
|
|
/// </summary>
|
|
public string Content
|
|
{
|
|
get => _content;
|
|
private set
|
|
{
|
|
if (SetValue(ref _content, value))
|
|
RaisePropertyChanged(nameof(HasText));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 是否有可展示的文字内容。
|
|
/// </summary>
|
|
public bool HasText => !string.IsNullOrWhiteSpace(Content);
|
|
|
|
/// <summary>
|
|
/// 消息是否已撤回。
|
|
/// </summary>
|
|
public bool IsRecalled
|
|
{
|
|
get => _isRecalled;
|
|
private set => SetValue(ref _isRecalled, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 消息发送时间。
|
|
/// </summary>
|
|
public DateTimeOffset CreatedAt
|
|
{
|
|
get => _createdAt;
|
|
private set
|
|
{
|
|
if (SetValue(ref _createdAt, value))
|
|
RaisePropertyChanged(nameof(CreatedAtText));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 消息中的公开图片附件。
|
|
/// </summary>
|
|
public ObservableCollection<ChatAttachmentViewModel> Attachments { get; }
|
|
|
|
/// <summary>
|
|
/// 是否包含图片附件。
|
|
/// </summary>
|
|
public bool HasAttachments => Attachments.Count > 0;
|
|
|
|
/// <summary>
|
|
/// 当前消息是否由客户发送。
|
|
/// </summary>
|
|
public bool IsCustomer =>
|
|
string.Equals(SenderType, "customer", StringComparison.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// 适合聊天界面展示的本地时间文本。
|
|
/// </summary>
|
|
public string CreatedAtText =>
|
|
CreatedAt == default
|
|
? string.Empty
|
|
: CreatedAt.ToLocalTime().ToString("MM-dd HH:mm", CultureInfo.CurrentCulture);
|
|
|
|
/// <summary>
|
|
/// 使用服务端最新数据更新消息。短时图片地址会随每次查询变化,因此需要刷新附件。
|
|
/// </summary>
|
|
public void UpdateFrom(TicketMessageDto dto)
|
|
{
|
|
if (dto == null)
|
|
return;
|
|
|
|
SenderDisplayName = string.IsNullOrWhiteSpace(dto.SenderName)
|
|
? (IsCustomer ? "我" : "客服")
|
|
: dto.SenderName;
|
|
IsRecalled = dto.Recalled;
|
|
Content = dto.Recalled
|
|
? "该消息已撤回"
|
|
: (dto.Text ?? string.Empty);
|
|
CreatedAt = ParseTimestamp(dto.CreatedAt);
|
|
ReplaceAttachments(dto.Attachments);
|
|
}
|
|
|
|
private void ReplaceAttachments(IEnumerable<TicketAttachmentDto> attachments)
|
|
{
|
|
Attachments.Clear();
|
|
foreach (var dto in attachments ?? Enumerable.Empty<TicketAttachmentDto>())
|
|
{
|
|
if (dto == null)
|
|
continue;
|
|
Attachments.Add(new ChatAttachmentViewModel(dto));
|
|
}
|
|
RaisePropertyChanged(nameof(HasAttachments));
|
|
_ = LoadAttachmentsAsync();
|
|
}
|
|
|
|
private async Task LoadAttachmentsAsync()
|
|
{
|
|
try
|
|
{
|
|
await Task.WhenAll(Attachments.Select(item => item.LoadAsync()));
|
|
}
|
|
catch
|
|
{
|
|
// 单张图片加载失败时保留占位提示,不影响文字留言显示。
|
|
}
|
|
}
|
|
|
|
private static DateTimeOffset ParseTimestamp(string value)
|
|
{
|
|
if (DateTimeOffset.TryParse(
|
|
value,
|
|
CultureInfo.InvariantCulture,
|
|
DateTimeStyles.AssumeUniversal,
|
|
out var parsed))
|
|
{
|
|
return parsed;
|
|
}
|
|
return default;
|
|
}
|
|
}
|