using WxOrder.Framework.Commons; using _29WxOrderWpf.Ctrls.Statistics.Models; using _29WxOrderWpf.Ctrls.Statistics.Services; using AllBase.Model.MvvmBaseClasses; 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 WxOrder.Framework.Commons; namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels { /// /// 天/周/月周期统计 - 页面 ViewModel(MVVM)。 /// 通过 PeriodStatisticType 指定统计周期(Day/Week/Month), /// 页面统一展示:筛选条件 + 周期×店铺统计表格 + 分页。 /// public class PeriodRevenueStatisticsViewModel : PropertyNotifyObject { /// 每页行数 private const int PageSize = 50; 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 ObservableCollection _selectedShops; private ShopRevenueSummary _summary; private ObservableCollection _statistics; private List _allItems = new List(); private int _pageIndex = 1; private int _totalPages = 1; private int _totalCount; private string _pagerText = string.Empty; /// 是否已完成首次初始化(用于避免页签来回切换时重复初始化、重复查询) private bool _initialized; /// 上一次真正生效(并据此计算过日期)的时间范围选项,避免同一个选项被重复应用 private TimeRangeOption _appliedTimeRange; 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(GetTimeRangeOptions(periodType)); ShopOptions = new ObservableCollection(); 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); } /// /// 页面首次显示时初始化查询条件并加载数据(由外层 TabControl 的选中事件调用)。 /// 重复调用(在“综合统计 / 天统计 / 周统计 / 月统计”页签之间来回切换)会被忽略: /// 否则每次切回来都会把时间范围和店铺重置成默认值,并重新发起一次查询。 /// 需要刷新数据时点“查询”按钮即可。 /// public void Init() { if (_initialized) return; _initialized = true; InitializeAsync(); } #region 页面标题 /// 页面标题(天统计/周统计/月统计) public string PageTitle { get; } /// 周期列表头(日期/周/月份) 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 时间范围选项 /// 按统计周期返回对应的时间范围选项 private static List GetTimeRangeOptions(PeriodStatisticType periodType) { switch (periodType) { case PeriodStatisticType.Week: return new List { 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 { 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 { 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" } }; } } /// 按统计周期返回默认选中的时间范围值 private static string GetDefaultTimeRangeValue(PeriodStatisticType periodType) { switch (periodType) { case PeriodStatisticType.Week: return "week"; case PeriodStatisticType.Month: return "month"; default: return "last10"; } } #endregion #region 查询条件 /// 时间范围选项 public ObservableCollection TimeRangeOptions { get; } /// 选中的时间范围 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)); } } /// 是否为自定义时间范围 public bool IsCustomDateRange { get { return SelectedTimeRange != null && SelectedTimeRange.Value == "custom"; } } /// 开始日期 public DateTime StartDate { get { return this.GetValue(x => x.StartDate); } set { this.SetValue(x => x.StartDate, value); } } /// 结束日期 public DateTime EndDate { get { return this.GetValue(x => x.EndDate); } set { this.SetValue(x => x.EndDate, value); } } /// 店铺选项(“全部店铺”由多选控件“全部商铺”表示) public ObservableCollection ShopOptions { get; } /// 选中的店铺(多选,绑定 ShopFilterComboBox.SelectedItems) public ObservableCollection SelectedShops { get { return _selectedShops; } set { _selectedShops = value; OnPropertyChanged(nameof(SelectedShops)); } } #endregion #region 统计结果 /// 汇总卡片数据 public ShopRevenueSummary Summary { get { return _summary; } private set { _summary = value; OnPropertyChanged(nameof(Summary)); } } /// 当前页的周期×店铺统计表格数据 public ObservableCollection Statistics { get { return _statistics; } private set { _statistics = value; OnPropertyChanged(nameof(Statistics)); } } /// 是否正在加载 public bool IsLoading { get { return this.GetValue(x => x.IsLoading); } set { this.SetValue(x => x.IsLoading, value); } } #endregion #region 分页 /// 总行数 public int TotalCount { get { return _totalCount; } private set { _totalCount = value; OnPropertyChanged(nameof(TotalCount)); } } /// 当前页码(从 1 开始) public int PageIndex { get { return _pageIndex; } private set { _pageIndex = value; OnPropertyChanged(nameof(PageIndex)); } } /// 总页数 public int TotalPages { get { return _totalPages; } private set { _totalPages = value; OnPropertyChanged(nameof(TotalPages)); } } /// 分页提示文本(共 X 条 第 Y / Z 页) public string PagerText { get { return _pagerText; } private set { _pagerText = value; OnPropertyChanged(nameof(PagerText)); } } /// 是否有上一页 public bool HasPrevPage { get { return PageIndex > 1; } } /// 是否有下一页 public bool HasNextPage { get { return PageIndex < TotalPages; } } /// 是否多页 public bool HasMultiplePages { get { return TotalPages > 1; } } /// 首页命令 public ICommand FirstPageCommand { get { return _firstPageCommand; } } /// 上一页命令 public ICommand PrevPageCommand { get { return _prevPageCommand; } } /// 下一页命令 public ICommand NextPageCommand { get { return _nextPageCommand; } } /// 末页命令 public ICommand LastPageCommand { get { return _lastPageCommand; } } /// 跳转到指定页并刷新当前页数据 private void GoToPage(int page) { if (page < 1) page = 1; if (page > TotalPages) page = TotalPages; PageIndex = page; ApplyPaging(); } /// 按当前页码截取当前页数据并刷新分页状态 private void ApplyPaging() { var pageItems = _allItems .Skip((PageIndex - 1) * PageSize) .Take(PageSize) .ToList(); Statistics = new ObservableCollection(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 命令 /// 查询命令 public ICommand QueryCommand { get; } /// 重置命令 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(ShopOptions); // 默认时间范围 SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == GetDefaultTimeRangeValue(_periodType)); ApplyTimeRange(); await LoadAsync(); } catch (Exception ex) { MessageBox.Show("初始化数据失败:" + ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Warning); } } #endregion #region 业务逻辑 /// 根据时间范围选项计算起止日期 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; } } /// /// 根据选中店铺生成查询用 ERP 店铺ID 列表(接口 ShopIds 使用 ShopLoginInfo.ErpShopId): /// 未选/全选/选中“全部店铺”时返回空列表(表示全部店铺)。 /// private List BuildSelectedShopIds() { var selected = SelectedShops; if (selected == null || selected.Count == 0) return new List(); var ids = selected.Select(s => s == null ? string.Empty : s.ErpShopId).ToList(); if (ids.Any(string.IsNullOrEmpty)) return new List(); if (selected.Count >= ShopOptions.Count) return new List(); return ids; } /// 组装查询参数(起止日期已保证先后顺序) private ShopRevenueStatisticsQuery BuildQuery() { if (StartDate > EndDate) { var temp = StartDate; StartDate = EndDate; EndDate = temp; } return new ShopRevenueStatisticsQuery { StartDate = StartDate, EndDate = EndDate, ShopIds = BuildSelectedShopIds(), }; } /// 执行查询,刷新周期统计表格并回到第一页 private async Task LoadAsync() { IsLoading = true; try { var result = await _service.QueryPeriodStatisticsAsync(BuildQuery(), _periodType); Summary = result.Summary; _allItems = result.Items ?? new List(); 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; } } /// 重置查询条件并重新加载 private async void Reset() { SelectedShops = new ObservableCollection(ShopOptions); SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == GetDefaultTimeRangeValue(_periodType)); ApplyTimeRange(); await LoadAsync(); } #endregion public ICommand ShowOrderListCommand => new AsyncDelegateCommand(ShowOrderList); private async Task ShowOrderList(string type) { var detailType = ParseDetailType(type); List showList; // 明细接口数据量大、耗时长,期间显示页面统一的加载遮罩,避免点了没反应 IsLoading = true; try { showList = await _service.QueryOrderRowsAsync(BuildQuery(), detailType) ?? new List(); } catch (Exception ex) { MessageBox.Show("订单明细查询失败:" + ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } finally { IsLoading = false; } if (showList.Count == 0) { MessageBox.Show("未查询到符合条件的订单明细。", "提示", MessageBoxButton.OK, MessageBoxImage.Information); return; } new DialogWindow() { Title = "订单列表", Content = new ShowOrderDialogView(showList, detailType, _service), DataContext = showList }.ShowDialog(); } /// 卡片“明细”按钮参数 → 订单明细类型(对应接口参数 OrderType) private static ShopOrderDetailType ParseDetailType(string type) { switch (type) { case "RefundOrderCount": return ShopOrderDetailType.Refund; //退款订单 case "BuyerRefundRemainingAmount": return ShopOrderDetailType.BuyerUnrefunded; //买手未退回金额的订单 default: return ShopOrderDetailType.Valid; //有效订单 } } } }