Billing/Billing.Shared/Views/RankPage.xaml.cs
2022-03-09 16:21:03 +08:00

283 lines
9.6 KiB
C#

using Billing.Models;
using Billing.Themes;
using Billing.UI;
using Microcharts;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms;
using Resource = Billing.Languages.Resource;
namespace Billing.Views
{
public partial class RankPage : BillingPage
{
private static readonly BindableProperty ChartProperty = Helper.Create<Chart, RankPage>(nameof(Chart));
private static readonly BindableProperty CategoryChartProperty = Helper.Create<Chart, RankPage>(nameof(CategoryChart));
private static readonly BindableProperty TopBillsProperty = Helper.Create<IList<UIBill>, RankPage>(nameof(TopBills));
private static readonly BindableProperty NoResultChartProperty = Helper.Create<bool, RankPage>(nameof(NoResultChart));
private static readonly BindableProperty NoResultCategoryChartProperty = Helper.Create<bool, RankPage>(nameof(NoResultCategoryChart));
private static readonly BindableProperty NoResultTopBillsProperty = Helper.Create<bool, RankPage>(nameof(NoResultTopBills));
public Chart Chart
{
get => (Chart)GetValue(ChartProperty);
set => SetValue(ChartProperty, value);
}
public Chart CategoryChart
{
get => (Chart)GetValue(CategoryChartProperty);
set => SetValue(CategoryChartProperty, value);
}
public IList<UIBill> TopBills
{
get => (IList<UIBill>)GetValue(TopBillsProperty);
set => SetValue(TopBillsProperty, value);
}
public bool NoResultChart
{
get => (bool)GetValue(NoResultChartProperty);
set => SetValue(NoResultChartProperty, value);
}
public bool NoResultCategoryChart
{
get => (bool)GetValue(NoResultCategoryChartProperty);
set => SetValue(NoResultCategoryChartProperty, value);
}
public bool NoResultTopBills
{
get => (bool)GetValue(NoResultTopBillsProperty);
set => SetValue(NoResultTopBillsProperty, value);
}
public Command LeftCommand { get; }
public Command RightCommand { get; }
public Command FilterCommand { get; }
public Command EditBilling { get; }
private DateTime current;
private DateTime end;
private IEnumerable<Bill> bills;
private CategoryType type = CategoryType.Spending;
private readonly SKTypeface font;
public RankPage()
{
LeftCommand = new Command(OnLeftCommand);
RightCommand = new Command(OnRightCommand);
EditBilling = new Command(OnEditBilling);
var style = SKFontManager.Default.GetFontStyles("PingFang SC");
if (style != null)
{
font = style.CreateTypeface(SKFontStyle.Normal);
}
InitializeComponent();
}
public override void OnLoaded()
{
SetMonth(DateTime.Today);
}
private void OnLeftCommand()
{
if (scroller.ScrollY > 0)
{
scroller.ScrollToAsync(0, 0, true);
}
SetMonth(current.AddMonths(-1));
}
private void OnRightCommand()
{
if (scroller.ScrollY > 0)
{
scroller.ScrollToAsync(0, 0, true);
}
SetMonth(current.AddMonths(1));
}
private async void OnEditBilling(object o)
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
if (o is UIBill bill)
{
var page = new AddBillPage(bill.Bill);
page.BillChecked += OnBillChecked;
await Navigation.PushAsync(page);
}
}
}
private void UpdateBill(UIBill bill)
{
bill.Icon = App.Categories.FirstOrDefault(c => c.Id == bill.Bill.CategoryId)?.Icon ?? BaseModel.ICON_DEFAULT;
bill.Name = bill.Bill.Name;
bill.DateCreation = bill.Bill.CreateTime;
bill.Amount = bill.Bill.Amount;
bill.Wallet = App.Accounts.FirstOrDefault(a => a.Id == bill.Bill.WalletId)?.Name;
}
private void OnBillChecked(object sender, Bill e)
{
var bill = TopBills.FirstOrDefault(b => b.Bill == e);
if (bill != null)
{
UpdateBill(bill);
}
Task.Run(App.WriteBills);
}
private async void SetMonth(DateTime date)
{
current = date.AddDays(1 - date.Day);
end = current.AddDays(DateTime.DaysInMonth(current.Year, current.Month));
var format = Resource.DateRangeFormat;
Title = current.ToString(format) + " ~ " + end.AddDays(-1).ToString(format);
var spending = type == CategoryType.Spending;
bills = await Task.Run(() => App.Bills.Where(b => (b.Amount > 0 ^ spending) && b.CreateTime >= current && b.CreateTime <= end));
var primaryColor = BaseTheme.CurrentPrimaryColor.ToSKColor();
var textColor = BaseTheme.CurrentSecondaryTextColor.ToSKColor();
_ = Task.Run(() => LoadReportChart(primaryColor, textColor));
_ = Task.Run(() => LoadCategoryChart(primaryColor, textColor));
_ = Task.Run(LoadTopBills);
}
private void LoadReportChart(SKColor primaryColor, SKColor textColor)
{
var entries = new List<ChartEntry>();
for (var day = current; day <= end; day = day.AddDays(1))
{
var daybills = bills.Where(b => Helper.IsSameDay(b.CreateTime, day));
decimal amount = Math.Abs(daybills.Sum(b => b.Amount));
if (amount > 0)
{
entries.Add(new((float)amount)
{
Label = day.ToString("MM-dd"),
ValueLabel = amount.ToString("#,##0.##"),
Color = primaryColor,
TextColor = textColor,
ValueLabelColor = textColor
});
}
}
MainThread.BeginInvokeOnMainThread(() =>
{
if (entries.Count > 0)
{
NoResultChart = false;
Chart = new LineChart
{
BackgroundColor = SKColors.Transparent,
LabelTextSize = 24,
Entries = entries
};
}
else
{
Chart = null;
NoResultChart = true;
}
});
}
private void LoadCategoryChart(SKColor primaryColor, SKColor textColor)
{
var entries = new List<ChartEntry>();
var groups = bills.GroupBy(b => b.CategoryId);
var dict = new Dictionary<string, decimal>();
decimal all = 0m;
foreach (var g in groups)
{
var categoryId = g.Key;
var category = App.Categories.FirstOrDefault(c => c.Id == categoryId);
if (category?.ParentId != null)
{
category = App.Categories.FirstOrDefault(c => c.Id == category.ParentId) ?? category;
}
if (category != null)
{
var total = Math.Abs(g.Sum(g => g.Amount));
all += total;
if (dict.ContainsKey(category.Name))
{
dict[category.Name] += total;
}
else
{
dict.Add(category.Name, total);
}
}
}
foreach (var kv in dict)
{
entries.Add(new((float)kv.Value)
{
Label = kv.Key,
ValueLabel = (kv.Value * 100 / all).ToString("0.#") + "%",
Color = primaryColor,
TextColor = textColor,
ValueLabelColor = textColor
});
}
MainThread.BeginInvokeOnMainThread(() =>
{
if (entries.Count > 0)
{
NoResultCategoryChart = false;
CategoryChart = new RadarChart
{
BackgroundColor = SKColors.Transparent,
LabelTextSize = 30,
Typeface = font,
Entries = entries
};
}
else
{
CategoryChart = null;
NoResultCategoryChart = true;
}
});
}
private void LoadTopBills()
{
List<UIBill> topBills = bills.OrderByDescending(b => Math.Abs(b.Amount)).Take(10).Select(b => Helper.WrapBill(b)).ToList();
MainThread.BeginInvokeOnMainThread(() =>
{
if (topBills.Count > 0)
{
NoResultTopBills = false;
TopBills = topBills;
}
else
{
TopBills = null;
NoResultTopBills = true;
}
});
}
}
}