Files
29WxOrder/29WxOrderWpf/Ctrls/Statistics/ViewModels/PeriodRevenueStatisticsViewModel.cs
T

529 lines
14 KiB
C#
Raw 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 System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows;
using AllBase.Model.MvvmBaseClasses;
using _29WxOrderWpf.Ctrls.Statistics.Models;
using _29WxOrderWpf.Ctrls.Statistics.Services;
using _29WxOrderWpf.Commands;
namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
{
/// <summary>
/// 天/周/月周期统计 - 页面 ViewModel(MVVM)。
/// 通过 PeriodStatisticType 指定统计周期(Day/Week/Month),
/// 页面统一展示:筛选条件 + 周期×店铺统计表格 + 分页。
/// </summary>
public class PeriodRevenueStatisticsViewModel : PropertyNotifyObject
{
/// <summary>每页行数</summary>
private const int PageSize = 20;
private readonly IShopRevenueStatisticsService _service;
private readonly PeriodStatisticType _periodType;
private readonly DelegateCommand _firstPageCommand;
private readonly DelegateCommand _prevPageCommand;
private readonly DelegateCommand _nextPageCommand;
private readonly DelegateCommand _lastPageCommand;
private ShopOption _selectedShop;
private ShopRevenueSummary _summary;
private ObservableCollection<ShopPeriodStatisticsItem> _statistics;
private List<ShopPeriodStatisticsItem> _allItems = new List<ShopPeriodStatisticsItem>();
private int _pageIndex = 1;
private int _totalPages = 1;
private int _totalCount;
private string _pagerText = string.Empty;
public PeriodRevenueStatisticsViewModel(PeriodStatisticType periodType)
: this(periodType, new ShopRevenueStatisticsService())
{
}
public PeriodRevenueStatisticsViewModel(PeriodStatisticType periodType, IShopRevenueStatisticsService service)
{
_periodType = periodType;
_service = service ?? new ShopRevenueStatisticsService();
PageTitle = GetPageTitle(periodType);
PeriodColumnHeader = GetPeriodColumnHeader(periodType);
TimeRangeOptions = new ObservableCollection<TimeRangeOption>(GetTimeRangeOptions(periodType));
ShopOptions = new ObservableCollection<ShopOption>();
QueryCommand = new DelegateCommand(async () => await LoadAsync());
ResetCommand = new DelegateCommand(Reset);
_firstPageCommand = new DelegateCommand(() => GoToPage(1), () => HasPrevPage);
_prevPageCommand = new DelegateCommand(() => GoToPage(PageIndex - 1), () => HasPrevPage);
_nextPageCommand = new DelegateCommand(() => GoToPage(PageIndex + 1), () => HasNextPage);
_lastPageCommand = new DelegateCommand(() => GoToPage(TotalPages), () => HasNextPage);
InitializeAsync();
}
#region 页面标题
/// <summary>页面标题(天统计/周统计/月统计)</summary>
public string PageTitle { get; }
/// <summary>周期列表头(日期/周/月份)</summary>
public string PeriodColumnHeader { get; }
private static string GetPageTitle(PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return "周统计";
case PeriodStatisticType.Month:
return "月统计";
default:
return "天统计";
}
}
private static string GetPeriodColumnHeader(PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return "周";
case PeriodStatisticType.Month:
return "月份";
default:
return "日期";
}
}
#endregion
#region 时间范围选项
/// <summary>按统计周期返回对应的时间范围选项</summary>
private static List<TimeRangeOption> GetTimeRangeOptions(PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return new List<TimeRangeOption>
{
new TimeRangeOption { Text = "本周", Value = "week" },
new TimeRangeOption { Text = "上周", Value = "lastweek" },
new TimeRangeOption { Text = "最近2周", Value = "last2weeks" },
new TimeRangeOption { Text = "最近4周", Value = "last4weeks" },
new TimeRangeOption { Text = "自定义", Value = "custom" }
};
case PeriodStatisticType.Month:
return new List<TimeRangeOption>
{
new TimeRangeOption { Text = "本月", Value = "month" },
new TimeRangeOption { Text = "上月", Value = "lastmonth" },
new TimeRangeOption { Text = "最近3个月", Value = "last3months" },
new TimeRangeOption { Text = "最近6个月", Value = "last6months" },
new TimeRangeOption { Text = "最近12个月", Value = "last12months" },
new TimeRangeOption { Text = "自定义", Value = "custom" }
};
default:
return new List<TimeRangeOption>
{
new TimeRangeOption { Text = "最近5天", Value = "last5" },
new TimeRangeOption { Text = "最近10天", Value = "last10" },
new TimeRangeOption { Text = "最近20天", Value = "last20" },
new TimeRangeOption { Text = "最近30天", Value = "last30" },
new TimeRangeOption { Text = "自定义", Value = "custom" }
};
}
}
/// <summary>按统计周期返回默认选中的时间范围值</summary>
private static string GetDefaultTimeRangeValue(PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return "week";
case PeriodStatisticType.Month:
return "month";
default:
return "last10";
}
}
#endregion
#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);
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 ShopOption SelectedShop
{
get { return _selectedShop; }
set
{
_selectedShop = value;
OnPropertyChanged(nameof(SelectedShop));
}
}
#endregion
#region 统计结果
/// <summary>汇总卡片数据</summary>
public ShopRevenueSummary Summary
{
get { return _summary; }
private set
{
_summary = value;
OnPropertyChanged(nameof(Summary));
}
}
/// <summary>当前页的周期×店铺统计表格数据</summary>
public ObservableCollection<ShopPeriodStatisticsItem> Statistics
{
get { return _statistics; }
private set
{
_statistics = value;
OnPropertyChanged(nameof(Statistics));
}
}
/// <summary>是否正在加载</summary>
public bool IsLoading
{
get { return this.GetValue(x => x.IsLoading); }
set { this.SetValue(x => x.IsLoading, value); }
}
#endregion
#region 分页
/// <summary>总行数</summary>
public int TotalCount
{
get { return _totalCount; }
private set
{
_totalCount = value;
OnPropertyChanged(nameof(TotalCount));
}
}
/// <summary>当前页码(从 1 开始)</summary>
public int PageIndex
{
get { return _pageIndex; }
private set
{
_pageIndex = value;
OnPropertyChanged(nameof(PageIndex));
}
}
/// <summary>总页数</summary>
public int TotalPages
{
get { return _totalPages; }
private set
{
_totalPages = value;
OnPropertyChanged(nameof(TotalPages));
}
}
/// <summary>分页提示文本(共 X 条 第 Y / Z 页)</summary>
public string PagerText
{
get { return _pagerText; }
private set
{
_pagerText = value;
OnPropertyChanged(nameof(PagerText));
}
}
/// <summary>是否有上一页</summary>
public bool HasPrevPage
{
get { return PageIndex > 1; }
}
/// <summary>是否有下一页</summary>
public bool HasNextPage
{
get { return PageIndex < TotalPages; }
}
/// <summary>是否多页</summary>
public bool HasMultiplePages
{
get { return TotalPages > 1; }
}
/// <summary>首页命令</summary>
public ICommand FirstPageCommand
{
get { return _firstPageCommand; }
}
/// <summary>上一页命令</summary>
public ICommand PrevPageCommand
{
get { return _prevPageCommand; }
}
/// <summary>下一页命令</summary>
public ICommand NextPageCommand
{
get { return _nextPageCommand; }
}
/// <summary>末页命令</summary>
public ICommand LastPageCommand
{
get { return _lastPageCommand; }
}
/// <summary>跳转到指定页并刷新当前页数据</summary>
private void GoToPage(int page)
{
if (page < 1)
page = 1;
if (page > TotalPages)
page = TotalPages;
PageIndex = page;
ApplyPaging();
}
/// <summary>按当前页码截取当前页数据并刷新分页状态</summary>
private void ApplyPaging()
{
var pageItems = _allItems
.Skip((PageIndex - 1) * PageSize)
.Take(PageSize)
.ToList();
Statistics = new ObservableCollection<ShopPeriodStatisticsItem>(pageItems);
PagerText = string.Format("共 {0} 条 第 {1} / {2} 页", TotalCount, PageIndex, TotalPages);
OnPropertyChanged(nameof(HasPrevPage));
OnPropertyChanged(nameof(HasNextPage));
OnPropertyChanged(nameof(HasMultiplePages));
_firstPageCommand.RaiseCanExecuteChanged();
_prevPageCommand.RaiseCanExecuteChanged();
_nextPageCommand.RaiseCanExecuteChanged();
_lastPageCommand.RaiseCanExecuteChanged();
}
#endregion
#region 命令
/// <summary>查询命令</summary>
public ICommand QueryCommand { get; }
/// <summary>重置命令</summary>
public ICommand ResetCommand { get; }
#endregion
#region 初始化
private async void InitializeAsync()
{
try
{
// 店铺选项
ShopOptions.Add(new ShopOption { AppKey = string.Empty, ShopName = "全部店铺" });
var shops = await _service.GetShopOptionsAsync();
foreach (var shop in shops)
ShopOptions.Add(shop);
SelectedShop = ShopOptions.FirstOrDefault();
// 默认时间范围
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == GetDefaultTimeRangeValue(_periodType));
ApplyTimeRange();
await LoadAsync();
}
catch (Exception ex)
{
MessageBox.Show("初始化数据失败:" + ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
#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 "last5":
StartDate = today.AddDays(-4);
EndDate = now;
break;
case "last10":
StartDate = today.AddDays(-9);
EndDate = now;
break;
case "last20":
StartDate = today.AddDays(-19);
EndDate = now;
break;
case "last30":
StartDate = today.AddDays(-29);
EndDate = now;
break;
case "week":
{
int diff = today.DayOfWeek == DayOfWeek.Sunday ? 6 : (int)today.DayOfWeek - 1;
StartDate = today.AddDays(-diff);
EndDate = now;
}
break;
case "lastweek":
{
int diff = today.DayOfWeek == DayOfWeek.Sunday ? 6 : (int)today.DayOfWeek - 1;
var thisWeekStart = today.AddDays(-diff);
StartDate = thisWeekStart.AddDays(-7);
EndDate = thisWeekStart.AddDays(-1).AddHours(23).AddMinutes(59).AddSeconds(59);
}
break;
case "last2weeks":
StartDate = today.AddDays(-13);
EndDate = now;
break;
case "last4weeks":
StartDate = today.AddDays(-27);
EndDate = now;
break;
case "month":
StartDate = new DateTime(today.Year, today.Month, 1);
EndDate = now;
break;
case "lastmonth":
{
var firstOfThisMonth = new DateTime(today.Year, today.Month, 1);
StartDate = firstOfThisMonth.AddMonths(-1);
EndDate = firstOfThisMonth.AddDays(-1).AddHours(23).AddMinutes(59).AddSeconds(59);
}
break;
case "last3months":
StartDate = new DateTime(today.Year, today.Month, 1).AddMonths(-2);
EndDate = now;
break;
case "last6months":
StartDate = new DateTime(today.Year, today.Month, 1).AddMonths(-5);
EndDate = now;
break;
case "last12months":
StartDate = new DateTime(today.Year, today.Month, 1).AddMonths(-11);
EndDate = now;
break;
default:
// 自定义:保持用户选择的日期不变
break;
}
}
/// <summary>执行查询,刷新周期统计表格并回到第一页</summary>
private async Task LoadAsync()
{
if (StartDate > EndDate)
{
var temp = StartDate;
StartDate = EndDate;
EndDate = temp;
}
IsLoading = true;
try
{
var query = new ShopRevenueStatisticsQuery
{
StartDate = StartDate,
EndDate = EndDate,
ShopKey = SelectedShop == null ? string.Empty : SelectedShop.AppKey,
};
var table = await _service.GetPeriodStatisticsAsync(query, _periodType);
Summary = await _service.GetSummaryAsync(query);
_allItems = table ?? new List<ShopPeriodStatisticsItem>();
TotalCount = _allItems.Count;
TotalPages = TotalCount == 0 ? 1 : (int)Math.Ceiling(TotalCount / (double)PageSize);
GoToPage(1);
}
catch (Exception ex)
{
// 查询异常:保留上一次数据,并弹框提示
MessageBox.Show("查询失败:" + ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
}
finally
{
IsLoading = false;
}
}
/// <summary>重置查询条件并重新加载</summary>
private async void Reset()
{
SelectedShop = ShopOptions.FirstOrDefault();
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == GetDefaultTimeRangeValue(_periodType));
ApplyTimeRange();
await LoadAsync();
}
#endregion
}
}