384 lines
11 KiB
C#
384 lines
11 KiB
C#
using WxOrder.Framework.Commons;
|
||
using WxOrder.Framework.Commons;
|
||
using _29WxOrderWpf.Ctrls.Statistics.Models;
|
||
using _29WxOrderWpf.Ctrls.Statistics.Services;
|
||
using AllBase.Model.MvvmBaseClasses;
|
||
using CefSharp;
|
||
using NPOI.SS.Formula.Functions;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Collections.ObjectModel;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
using System.Windows.Input;
|
||
using Work.BaseForms;
|
||
|
||
namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
|
||
{
|
||
/// <summary>
|
||
/// 店铺营收统计 - 页面 ViewModel(MVVM)。
|
||
/// 通过 IShopRevenueStatisticsService 调用数据接口;
|
||
/// 接入真实接口时传入自定义实现:new ShopRevenueStatisticsViewModel(你的服务实现)。
|
||
/// </summary>
|
||
public class ShopRevenueStatisticsViewModel : PropertyNotifyObject
|
||
{
|
||
private readonly IShopRevenueStatisticsService _service;
|
||
|
||
private ShopRevenueSummary _summary;
|
||
private ObservableCollection<RevenueTrendPoint> _trendData;
|
||
private ObservableCollection<ShopSalesProportionItem> _shopProportionData;
|
||
private ObservableCollection<ShopStatisticsItem> _shopStatistics;
|
||
private ObservableCollection<ShopOption> _selectedShops;
|
||
private string _selectedPlatform;
|
||
|
||
/// <summary>上一次真正生效(并据此计算过日期)的时间范围选项,避免同一个选项被重复应用</summary>
|
||
private TimeRangeOption _appliedTimeRange;
|
||
|
||
public ShopRevenueStatisticsViewModel()
|
||
: this(new ShopRevenueStatisticsService())
|
||
{
|
||
|
||
}
|
||
|
||
public ShopRevenueStatisticsViewModel(IShopRevenueStatisticsService service)
|
||
{
|
||
_service = service ?? new ShopRevenueStatisticsService();
|
||
|
||
TimeRangeOptions = new ObservableCollection<TimeRangeOption>
|
||
{
|
||
new TimeRangeOption { Text = "昨天", Value = "yesterday" },
|
||
new TimeRangeOption { Text = "本周", Value = "week" },
|
||
new TimeRangeOption { Text = "本月", Value = "month" },
|
||
new TimeRangeOption { Text = "最近10天", Value = "last10" },
|
||
new TimeRangeOption { Text = "自定义", Value = "custom" }
|
||
};
|
||
|
||
ShopOptions = new ObservableCollection<ShopOption>();
|
||
|
||
QueryCommand = new DelegateCommand(async () => await LoadAsync());
|
||
ResetCommand = new DelegateCommand(Reset);
|
||
|
||
InitializeAsync();
|
||
}
|
||
|
||
#region 查询条件
|
||
|
||
/// <summary>时间范围选项</summary>
|
||
public ObservableCollection<TimeRangeOption> TimeRangeOptions { get; }
|
||
|
||
/// <summary>选中的时间范围</summary>
|
||
public TimeRangeOption SelectedTimeRange
|
||
{
|
||
get { return this.GetValue(x => x.SelectedTimeRange); }
|
||
set
|
||
{
|
||
this.SetValue(x => x.SelectedTimeRange, value);
|
||
|
||
// 只有用户真正切换到“另一个”时间范围选项时才重新计算起止日期。
|
||
// 页签切换时下拉框会把同一个选项(甚至先 null 再回写)重放一遍,
|
||
// 若照样重算,就会把用户手工调过的日期覆盖掉。
|
||
if (value != null && !ReferenceEquals(_appliedTimeRange, value))
|
||
{
|
||
_appliedTimeRange = value;
|
||
ApplyTimeRange();
|
||
}
|
||
|
||
OnPropertyChanged(nameof(IsCustomDateRange));
|
||
}
|
||
}
|
||
|
||
/// <summary>是否为自定义时间范围</summary>
|
||
public bool IsCustomDateRange
|
||
{
|
||
get { return SelectedTimeRange != null && SelectedTimeRange.Value == "custom"; }
|
||
}
|
||
|
||
/// <summary>开始日期</summary>
|
||
public DateTime StartDate
|
||
{
|
||
get { return this.GetValue(x => x.StartDate); }
|
||
set { this.SetValue(x => x.StartDate, value); }
|
||
}
|
||
|
||
/// <summary>结束日期</summary>
|
||
public DateTime EndDate
|
||
{
|
||
get { return this.GetValue(x => x.EndDate); }
|
||
set { this.SetValue(x => x.EndDate, value); }
|
||
}
|
||
|
||
/// <summary>店铺选项(“全部店铺”由多选控件“全部商铺”表示)</summary>
|
||
public ObservableCollection<ShopOption> ShopOptions { get; }
|
||
|
||
/// <summary>选中的店铺</summary>
|
||
public ObservableCollection<ShopOption> SelectedShops
|
||
{
|
||
get { return _selectedShops; }
|
||
set
|
||
{
|
||
_selectedShops = value;
|
||
OnPropertyChanged(nameof(SelectedShops));
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 统计结果
|
||
|
||
/// <summary>汇总卡片数据</summary>
|
||
public ShopRevenueSummary Summary
|
||
{
|
||
get { return _summary; }
|
||
private set
|
||
{
|
||
_summary = value;
|
||
OnPropertyChanged(nameof(Summary));
|
||
}
|
||
}
|
||
|
||
/// <summary>销售趋势数据</summary>
|
||
public ObservableCollection<RevenueTrendPoint> TrendData
|
||
{
|
||
get { return _trendData; }
|
||
private set
|
||
{
|
||
_trendData = value;
|
||
OnPropertyChanged(nameof(TrendData));
|
||
}
|
||
}
|
||
|
||
/// <summary>店铺销售占比数据</summary>
|
||
public ObservableCollection<ShopSalesProportionItem> ShopProportionData
|
||
{
|
||
get { return _shopProportionData; }
|
||
private set
|
||
{
|
||
_shopProportionData = value;
|
||
OnPropertyChanged(nameof(ShopProportionData));
|
||
}
|
||
}
|
||
|
||
/// <summary>店铺统计表格数据</summary>
|
||
public ObservableCollection<ShopStatisticsItem> ShopStatistics
|
||
{
|
||
get { return _shopStatistics; }
|
||
private set
|
||
{
|
||
_shopStatistics = value;
|
||
OnPropertyChanged(nameof(ShopStatistics));
|
||
}
|
||
}
|
||
|
||
/// <summary>是否正在加载</summary>
|
||
public bool IsLoading
|
||
{
|
||
get { return this.GetValue(x => x.IsLoading); }
|
||
set { this.SetValue(x => x.IsLoading, value); }
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令
|
||
|
||
/// <summary>查询命令</summary>
|
||
public ICommand QueryCommand { get; }
|
||
|
||
/// <summary>重置命令</summary>
|
||
public ICommand ResetCommand { get; }
|
||
|
||
#endregion
|
||
|
||
#region 初始化
|
||
|
||
private async void InitializeAsync()
|
||
{
|
||
try
|
||
{
|
||
// 店铺选项(全部店铺由多选控件“全部商铺”表示,不再额外加入“全部店铺”项)
|
||
var shops = await _service.GetShopOptionsAsync();
|
||
foreach (var shop in shops)
|
||
ShopOptions.Add(shop);
|
||
SelectedShops = new ObservableCollection<ShopOption>(ShopOptions);
|
||
|
||
// 默认时间范围:本月
|
||
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == "month");
|
||
ApplyTimeRange();
|
||
|
||
await LoadAsync();
|
||
}
|
||
catch (Exception)
|
||
{
|
||
// 初始化失败(如接口不可用),页面保持可查询状态;可在此接入日志/提示
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 业务逻辑
|
||
|
||
/// <summary>根据时间范围选项计算起止日期</summary>
|
||
private void ApplyTimeRange()
|
||
{
|
||
var now = DateTime.Now;
|
||
var today = now.Date;
|
||
var selected = SelectedTimeRange == null ? string.Empty : SelectedTimeRange.Value;
|
||
switch (selected)
|
||
{
|
||
case "yesterday":
|
||
StartDate = today.AddDays(-1);
|
||
EndDate = today.AddDays(-1).AddHours(23).AddMinutes(59).AddSeconds(59);
|
||
break;
|
||
case "week":
|
||
int diff = today.DayOfWeek == DayOfWeek.Sunday ? 6 : (int)today.DayOfWeek - 1;
|
||
StartDate = today.AddDays(-diff);
|
||
EndDate = now;
|
||
break;
|
||
case "month":
|
||
StartDate = new DateTime(today.Year, today.Month, 1);
|
||
EndDate = now;
|
||
break;
|
||
case "last10":
|
||
StartDate = today.AddDays(-9);
|
||
EndDate = now;
|
||
break;
|
||
default:
|
||
// 自定义:保持用户选择的日期不变
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据选中店铺生成查询用 ERP 店铺ID 列表(接口 ShopIds 使用 ShopLoginInfo.ErpShopId):
|
||
/// 未选/全选/选中“全部店铺”时返回空列表(表示全部店铺)。
|
||
/// </summary>
|
||
private List<string> BuildSelectedShopIds()
|
||
{
|
||
var selected = SelectedShops;
|
||
if (selected == null || selected.Count == 0)
|
||
return new List<string>();
|
||
|
||
var ids = selected.Select(s => s == null ? string.Empty : s.ErpShopId).ToList();
|
||
if (ids.Any(string.IsNullOrEmpty))
|
||
return new List<string>();
|
||
|
||
if (selected.Count >= ShopOptions.Count)
|
||
return new List<string>();
|
||
|
||
return ids;
|
||
}
|
||
|
||
/// <summary>组装查询参数(起止日期已保证先后顺序)</summary>
|
||
private ShopRevenueStatisticsQuery BuildQuery()
|
||
{
|
||
if (StartDate == EndDate)
|
||
{
|
||
EndDate = EndDate.AddDays(1).AddSeconds(-1);
|
||
}
|
||
else if (StartDate > EndDate)
|
||
{
|
||
var temp = StartDate;
|
||
StartDate = EndDate;
|
||
EndDate = temp;
|
||
}
|
||
|
||
|
||
return new ShopRevenueStatisticsQuery
|
||
{
|
||
StartDate = StartDate,
|
||
EndDate = EndDate,
|
||
ShopIds = BuildSelectedShopIds(),
|
||
};
|
||
}
|
||
|
||
/// <summary>执行查询,刷新页面所有统计数据</summary>
|
||
private async Task LoadAsync()
|
||
{
|
||
IsLoading = true;
|
||
try
|
||
{
|
||
var result = await _service.QueryAsync(BuildQuery());
|
||
|
||
Summary = result.Summary;
|
||
TrendData = new ObservableCollection<RevenueTrendPoint>(result.Trend ?? new List<RevenueTrendPoint>());
|
||
ShopProportionData = new ObservableCollection<ShopSalesProportionItem>(result.ShopProportions ?? new List<ShopSalesProportionItem>());
|
||
ShopStatistics = new ObservableCollection<ShopStatisticsItem>(result.ShopStatistics ?? new List<ShopStatisticsItem>());
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 查询异常:保留上一次数据,并弹框提示
|
||
BaseMsgForm.ShowError("查询失败:" + ex.Message, "提示");
|
||
}
|
||
finally
|
||
{
|
||
IsLoading = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>重置查询条件并重新加载</summary>
|
||
private async void Reset()
|
||
{
|
||
SelectedShops = new ObservableCollection<ShopOption>(ShopOptions);
|
||
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == "month");
|
||
ApplyTimeRange();
|
||
await LoadAsync();
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
public ICommand ShowOrderListCommand => new AsyncDelegateCommand<string>(ShowOrderList);
|
||
|
||
private async Task ShowOrderList(string type)
|
||
{
|
||
var detailType = ParseDetailType(type);
|
||
List<ShopOrderDetailResponse> showList;
|
||
IsLoading = true;
|
||
try
|
||
{
|
||
showList = await _service.QueryOrderRowsAsync(BuildQuery(), detailType)
|
||
?? new List<ShopOrderDetailResponse>();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
BaseMsgForm.ShowError("订单明细查询失败:" + ex.Message, "提示");
|
||
return;
|
||
}
|
||
finally
|
||
{
|
||
IsLoading = false;
|
||
}
|
||
|
||
if (showList.Count == 0)
|
||
{
|
||
BaseMsgForm.ShowInfo("未查询到符合条件的订单明细。", "提示");
|
||
return;
|
||
}
|
||
|
||
new DialogWindow(GetWindow())
|
||
{
|
||
Title = "订单列表",
|
||
Content = new ShowOrderDialogView(showList, detailType, _service),
|
||
DataContext = showList
|
||
}.Show();
|
||
}
|
||
|
||
/// <summary>卡片“明细”按钮参数 → 订单明细类型(对应接口参数 OrderType)</summary>
|
||
private static ShopOrderDetailType ParseDetailType(string type)
|
||
{
|
||
switch (type)
|
||
{
|
||
case "RefundOrderCount":
|
||
return ShopOrderDetailType.Refund; //退款订单
|
||
case "BuyerRefundRemainingAmount":
|
||
return ShopOrderDetailType.BuyerUnrefunded; //买手未退回金额的订单
|
||
case "ProfitOrder":
|
||
return ShopOrderDetailType.Profit; //买手未退回金额的订单
|
||
default:
|
||
return ShopOrderDetailType.Valid; //有效订单
|
||
}
|
||
}
|
||
|
||
public Func<Window> GetWindow;
|
||
}
|
||
} |