170 lines
4.2 KiB
C#
170 lines
4.2 KiB
C#
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";
|
||
}
|
||
}
|