Files
2026-10-20 17:03:23 +08:00

170 lines
4.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using OrderSupportTicket.Models.Api;
using OrderSupportTicket.Services;
using System;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using WPFCommon.Models;
namespace OrderSupportTicket.ViewModels;
/// <summary>
/// 留言气泡中的公开图片附件。
/// </summary>
public sealed class ChatAttachmentViewModel : BindableBaseEx
{
private bool _isLoading;
private bool _isLoadFailed;
private ImageSource _image;
public ChatAttachmentViewModel(TicketAttachmentDto dto)
{
if (dto == null)
throw new ArgumentNullException(nameof(dto));
Id = dto.Id;
OriginalName = dto.OriginalName;
ContentType = dto.ContentType;
ByteSize = dto.ByteSize;
ReadUrl = dto.ReadUrl;
}
/// <summary>
/// 附件唯一 ID。
/// </summary>
public string Id { get; }
/// <summary>
/// 原始文件名。
/// </summary>
public string OriginalName { get; }
/// <summary>
/// 图片 MIME 类型。
/// </summary>
public string ContentType { get; }
/// <summary>
/// 图片字节数。
/// </summary>
public long ByteSize { get; }
/// <summary>
/// 服务端生成的短时读取地址。
/// </summary>
public string ReadUrl { get; }
/// <summary>
/// 已下载并解码的图片;加载完成前为空。
/// </summary>
public ImageSource Image
{
get => _image;
private set
{
if (SetValue(ref _image, value))
RaisePropertyChanged(nameof(HasImage));
}
}
/// <summary>
/// 是否已成功显示图片。
/// </summary>
public bool HasImage => Image != null;
/// <summary>
/// 图片是否已经确认加载失败。加载完成前保持为 false。
/// </summary>
public bool IsLoadFailed
{
get => _isLoadFailed;
private set
{
if (SetValue(ref _isLoadFailed, value))
RaisePropertyChanged(nameof(ToolTipText));
}
}
/// <summary>
/// 附件名称、大小与加载状态提示。
/// </summary>
public string ToolTipText
{
get
{
var name = string.IsNullOrWhiteSpace(OriginalName) ? "图片" : OriginalName;
var size = FormatSize(ByteSize);
if (string.IsNullOrWhiteSpace(ReadUrl))
return name + "(" + size + ",暂不可用)";
if (_isLoading)
return name + "(" + size + ",加载中…)";
if (IsLoadFailed)
return name + "(" + size + ",加载失败)";
return name + "(" + size + ")";
}
}
/// <summary>
/// 下载并解码短时读取地址。失败时保留占位提示,不阻断文字留言展示。
/// </summary>
public async Task LoadAsync()
{
if (Image != null || _isLoading)
return;
if (string.IsNullOrWhiteSpace(ReadUrl))
{
IsLoadFailed = true;
return;
}
IsLoadFailed = false;
_isLoading = true;
RaisePropertyChanged(nameof(ToolTipText));
try
{
var image = await ProductImageLoader.LoadAsync(ReadUrl);
if (image == null)
IsLoadFailed = true;
else
await SetImageAsync(image);
}
catch
{
IsLoadFailed = true;
}
finally
{
_isLoading = false;
RaisePropertyChanged(nameof(ToolTipText));
}
}
private async Task SetImageAsync(ImageSource image)
{
if (image == null)
return;
var dispatcher = Application.Current?.Dispatcher;
if (dispatcher == null || dispatcher.CheckAccess())
{
Image = image;
IsLoadFailed = false;
return;
}
await dispatcher.InvokeAsync(() =>
{
Image = image;
IsLoadFailed = false;
});
}
private static string FormatSize(long byteSize)
{
if (byteSize >= 1024 * 1024)
return (byteSize / 1024d / 1024d).ToString("0.0", CultureInfo.CurrentCulture) + " MB";
if (byteSize >= 1024)
return (byteSize / 1024d).ToString("0.0", CultureInfo.CurrentCulture) + " KB";
return byteSize.ToString(CultureInfo.CurrentCulture) + " B";
}
}