complete account page
This commit is contained in:
parent
aa38e186c7
commit
51f8e4f2e8
@ -1,4 +1,8 @@
|
|||||||
using Billing.Languages;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Billing.Languages;
|
||||||
|
using Billing.Models;
|
||||||
|
using Billing.Store;
|
||||||
using Billing.Themes;
|
using Billing.Themes;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@ -9,6 +13,13 @@ namespace Billing
|
|||||||
{
|
{
|
||||||
public static AppTheme CurrentTheme { get; private set; }
|
public static AppTheme CurrentTheme { get; private set; }
|
||||||
public static PlatformCulture CurrentCulture { get; private set; }
|
public static PlatformCulture CurrentCulture { get; private set; }
|
||||||
|
public static List<Bill> Bills => bills ??= new List<Bill>();
|
||||||
|
public static List<Account> Accounts => accounts ??= new List<Account>();
|
||||||
|
public static List<Category> Categories => categories ??= new List<Category>();
|
||||||
|
|
||||||
|
private static List<Bill> bills;
|
||||||
|
private static List<Account> accounts;
|
||||||
|
private static List<Category> categories;
|
||||||
|
|
||||||
public App()
|
public App()
|
||||||
{
|
{
|
||||||
@ -19,8 +30,21 @@ namespace Billing
|
|||||||
Shell.Current.GoToAsync("//Bills");
|
Shell.Current.GoToAsync("//Bills");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void WriteAccounts() => StoreHelper.WriteAccounts(accounts);
|
||||||
|
|
||||||
|
public static void WriteBills() => StoreHelper.WriteBills(bills);
|
||||||
|
|
||||||
protected override void OnStart()
|
protected override void OnStart()
|
||||||
{
|
{
|
||||||
|
Helper.Debug($"personal folder: {StoreHelper.PersonalFolder}");
|
||||||
|
Helper.Debug($"cache folder: {StoreHelper.CacheFolder}");
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
accounts = StoreHelper.GetAccounts();
|
||||||
|
categories = StoreHelper.GetCategories();
|
||||||
|
bills = StoreHelper.GetBills();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnResume()
|
protected override void OnResume()
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
<DependentUpon>MainShell.xaml</DependentUpon>
|
<DependentUpon>MainShell.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Models\BaseModel.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Models\BaseModel.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Models\Billing.cs" />
|
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Models\Category.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Models\Category.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Models\Account.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Models\Account.cs" />
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)Themes\BaseTheme.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)Themes\BaseTheme.cs" />
|
||||||
@ -59,6 +58,8 @@
|
|||||||
<DependentUpon>SettingPage.xaml</DependentUpon>
|
<DependentUpon>SettingPage.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="$(MSBuildThisFileDirectory)UI\OptionsCells.cs" />
|
<Compile Include="$(MSBuildThisFileDirectory)UI\OptionsCells.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Store\StoreHelper.cs" />
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)Models\Bill.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)MainShell.xaml">
|
<EmbeddedResource Include="$(MSBuildThisFileDirectory)MainShell.xaml">
|
||||||
@ -95,4 +96,7 @@
|
|||||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="$(MSBuildThisFileDirectory)Store\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -1,9 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
namespace Billing
|
namespace Billing
|
||||||
{
|
{
|
||||||
internal static class Helper
|
internal static class Helper
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
public static void Debug(string message)
|
public static void Debug(string message)
|
||||||
{
|
{
|
||||||
var time = DateTime.Now.ToString("HH:mm:ss.fff");
|
var time = DateTime.Now.ToString("HH:mm:ss.fff");
|
||||||
@ -20,5 +22,36 @@ namespace Billing
|
|||||||
var time = DateTime.Now.ToString("HH:mm:ss.fff");
|
var time = DateTime.Now.ToString("HH:mm:ss.fff");
|
||||||
System.Diagnostics.Debug.Fail($"[{time}] - {category}", message);
|
System.Diagnostics.Debug.Fail($"[{time}] - {category}", message);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
#pragma warning disable IDE0060 // Remove unused parameter
|
||||||
|
public static void Debug(string message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Error(string category, Exception ex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Error(string category, string message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#pragma warning restore IDE0060 // Remove unused parameter
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public static bool NetworkAvailable
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Connectivity.NetworkAccess == NetworkAccess.Internet
|
||||||
|
|| Connectivity.NetworkAccess == NetworkAccess.ConstrainedInternet;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,6 +14,8 @@ namespace Billing.Languages
|
|||||||
public static string CreditCard => Text(nameof(CreditCard));
|
public static string CreditCard => Text(nameof(CreditCard));
|
||||||
public static string DebitCard => Text(nameof(DebitCard));
|
public static string DebitCard => Text(nameof(DebitCard));
|
||||||
public static string ElecAccount => Text(nameof(ElecAccount));
|
public static string ElecAccount => Text(nameof(ElecAccount));
|
||||||
|
public static string AddBill => Text(nameof(AddBill));
|
||||||
|
public static string EditBill => Text(nameof(EditBill));
|
||||||
|
|
||||||
static readonly Dictionary<string, LanguageResource> dict = new();
|
static readonly Dictionary<string, LanguageResource> dict = new();
|
||||||
|
|
||||||
|
@ -32,4 +32,8 @@
|
|||||||
<DebitCard>Debit Card</DebitCard>
|
<DebitCard>Debit Card</DebitCard>
|
||||||
<ElecAccount>Electronic Account</ElecAccount>
|
<ElecAccount>Electronic Account</ElecAccount>
|
||||||
<IconSelector>Icon Selection</IconSelector>
|
<IconSelector>Icon Selection</IconSelector>
|
||||||
|
<AddBill>Add Billing</AddBill>
|
||||||
|
<EditBill>Edit Billing</EditBill>
|
||||||
|
<Account>Account</Account>
|
||||||
|
<CreatedTime>Created Time</CreatedTime>
|
||||||
</root>
|
</root>
|
@ -32,4 +32,8 @@
|
|||||||
<DebitCard>储蓄卡</DebitCard>
|
<DebitCard>储蓄卡</DebitCard>
|
||||||
<ElecAccount>电子账户</ElecAccount>
|
<ElecAccount>电子账户</ElecAccount>
|
||||||
<IconSelector>图标选择</IconSelector>
|
<IconSelector>图标选择</IconSelector>
|
||||||
|
<AddBill>增加账单</AddBill>
|
||||||
|
<EditBill>编辑账单</EditBill>
|
||||||
|
<Account>账户</Account>
|
||||||
|
<CreatedTime>创建时间</CreatedTime>
|
||||||
</root>
|
</root>
|
@ -161,11 +161,12 @@ namespace Billing.Models
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
XDocument xdoc = ToXml();
|
|
||||||
using MemoryStream ms = new();
|
using MemoryStream ms = new();
|
||||||
using StreamWriter writer = new(ms, Encoding.UTF8);
|
//using StreamWriter writer = new(ms, Encoding.UTF8);
|
||||||
xdoc.Save(writer, SaveOptions.DisableFormatting);
|
//XDocument xdoc = ToXml();
|
||||||
writer.Flush();
|
//xdoc.Save(writer, SaveOptions.DisableFormatting);
|
||||||
|
//writer.Flush();
|
||||||
|
SaveToStream(ms);
|
||||||
ms.Seek(0, SeekOrigin.Begin);
|
ms.Seek(0, SeekOrigin.Begin);
|
||||||
using StreamReader reader = new(ms, Encoding.UTF8);
|
using StreamReader reader = new(ms, Encoding.UTF8);
|
||||||
return reader.ReadToEnd();
|
return reader.ReadToEnd();
|
||||||
|
@ -3,7 +3,7 @@ using System.Xml.Linq;
|
|||||||
|
|
||||||
namespace Billing.Models
|
namespace Billing.Models
|
||||||
{
|
{
|
||||||
public class Billing : BaseModel
|
public class Bill : BaseModel
|
||||||
{
|
{
|
||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
@ -5,6 +5,7 @@ namespace Billing.Models
|
|||||||
public class Category : BaseModel
|
public class Category : BaseModel
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
public CategoryType Type { get; set; }
|
||||||
public string Icon { get; set; } = ICON_DEFAULT;
|
public string Icon { get; set; } = ICON_DEFAULT;
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public int? ParentId { get; set; }
|
public int? ParentId { get; set; }
|
||||||
@ -12,6 +13,7 @@ namespace Billing.Models
|
|||||||
public override void OnXmlDeserialize(XElement node)
|
public override void OnXmlDeserialize(XElement node)
|
||||||
{
|
{
|
||||||
Id = Read(node, nameof(Id), 0);
|
Id = Read(node, nameof(Id), 0);
|
||||||
|
Type = (CategoryType)Read(node, nameof(Type), 0);
|
||||||
Icon = Read(node, nameof(Icon), ICON_DEFAULT);
|
Icon = Read(node, nameof(Icon), ICON_DEFAULT);
|
||||||
Name = Read(node, nameof(Name), string.Empty);
|
Name = Read(node, nameof(Name), string.Empty);
|
||||||
var parentId = Read(node, nameof(ParentId), -1);
|
var parentId = Read(node, nameof(ParentId), -1);
|
||||||
@ -24,6 +26,7 @@ namespace Billing.Models
|
|||||||
public override void OnXmlSerialize(XElement node)
|
public override void OnXmlSerialize(XElement node)
|
||||||
{
|
{
|
||||||
Write(node, nameof(Id), Id);
|
Write(node, nameof(Id), Id);
|
||||||
|
Write(node, nameof(Type), (int)Type);
|
||||||
Write(node, nameof(Icon), Icon);
|
Write(node, nameof(Icon), Icon);
|
||||||
Write(node, nameof(Name), Name);
|
Write(node, nameof(Name), Name);
|
||||||
if (ParentId != null)
|
if (ParentId != null)
|
||||||
@ -32,4 +35,10 @@ namespace Billing.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum CategoryType
|
||||||
|
{
|
||||||
|
Spending,
|
||||||
|
Income
|
||||||
|
}
|
||||||
}
|
}
|
103
Billing.Shared/Store/StoreHelper.cs
Normal file
103
Billing.Shared/Store/StoreHelper.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Billing.Models;
|
||||||
|
using Billing.UI;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
|
||||||
|
namespace Billing.Store
|
||||||
|
{
|
||||||
|
public class StoreHelper
|
||||||
|
{
|
||||||
|
public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
|
||||||
|
public static readonly string CacheFolder = FileSystem.CacheDirectory;
|
||||||
|
|
||||||
|
private const string accountFile = "accounts.xml";
|
||||||
|
private const string billFile = "bills.xml";
|
||||||
|
private const string categoryFile = "categories.xml";
|
||||||
|
|
||||||
|
private static StoreHelper instance;
|
||||||
|
private static StoreHelper Instance => instance ??= new StoreHelper();
|
||||||
|
|
||||||
|
public static List<Account> GetAccounts() => Instance.GetAccountsInternal();
|
||||||
|
public static void WriteAccounts(IEnumerable<Account> accounts) => Instance.WriteAccountsInternal(accounts);
|
||||||
|
public static List<Bill> GetBills() => Instance.GetBillsInternal();
|
||||||
|
public static void WriteBills(IEnumerable<Bill> bills) => Instance.WriteBillsInternal(bills);
|
||||||
|
public static List<Category> GetCategories() => Instance.GetCategoriesInternal();
|
||||||
|
public static void WriteCategories(IEnumerable<Category> categories) => Instance.WriteCategoriesInternal(categories);
|
||||||
|
|
||||||
|
private StoreHelper() { }
|
||||||
|
|
||||||
|
private List<Account> GetAccountsInternal()
|
||||||
|
{
|
||||||
|
return GetList<Account>(Path.Combine(PersonalFolder, accountFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteAccountsInternal(IEnumerable<Account> accounts)
|
||||||
|
{
|
||||||
|
var filename = Path.Combine(PersonalFolder, accountFile);
|
||||||
|
WriteList(filename, accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Bill> GetBillsInternal()
|
||||||
|
{
|
||||||
|
return GetList<Bill>(Path.Combine(PersonalFolder, billFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteBillsInternal(IEnumerable<Bill> bills)
|
||||||
|
{
|
||||||
|
var filename = Path.Combine(PersonalFolder, billFile);
|
||||||
|
WriteList(filename, bills);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Category> GetCategoriesInternal()
|
||||||
|
{
|
||||||
|
return GetList<Category>(Path.Combine(PersonalFolder, categoryFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteCategoriesInternal(IEnumerable<Category> categories)
|
||||||
|
{
|
||||||
|
var filename = Path.Combine(PersonalFolder, categoryFile);
|
||||||
|
WriteList(filename, categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Helper
|
||||||
|
|
||||||
|
private void WriteList<T>(string filename, IEnumerable<T> list) where T : IModel, new()
|
||||||
|
{
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var stream = File.OpenWrite(filename);
|
||||||
|
list.ToStream(stream);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Helper.Error("file.write", $"failed to write file: {filename}, error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<T> GetList<T>(string file) where T : IModel, new()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(file))
|
||||||
|
{
|
||||||
|
using var stream = File.OpenRead(file);
|
||||||
|
var list = ModelExtensionHelper.FromStream<T>(stream);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Helper.Error("file.read", $"failed to read file: {file}, error: {ex.Message}");
|
||||||
|
}
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@ namespace Billing.Themes
|
|||||||
public const string TextColor = nameof(TextColor);
|
public const string TextColor = nameof(TextColor);
|
||||||
public const string SecondaryTextColor = nameof(SecondaryTextColor);
|
public const string SecondaryTextColor = nameof(SecondaryTextColor);
|
||||||
public const string RedColor = nameof(RedColor);
|
public const string RedColor = nameof(RedColor);
|
||||||
|
public const string GreenColor = nameof(GreenColor);
|
||||||
|
|
||||||
protected abstract Color PrimaryMauiColor { get; }
|
protected abstract Color PrimaryMauiColor { get; }
|
||||||
protected abstract Color SecondaryMauiColor { get; }
|
protected abstract Color SecondaryMauiColor { get; }
|
||||||
|
@ -28,6 +28,7 @@ namespace Billing.Themes
|
|||||||
Add(TextColor, Color.FromRgb(0xcc, 0xcc, 0xcc));
|
Add(TextColor, Color.FromRgb(0xcc, 0xcc, 0xcc));
|
||||||
Add(SecondaryTextColor, Color.LightGray);
|
Add(SecondaryTextColor, Color.LightGray);
|
||||||
Add(RedColor, Color.FromRgb(211, 5, 5));
|
Add(RedColor, Color.FromRgb(211, 5, 5));
|
||||||
|
Add(GreenColor, Color.FromRgb(5, 211, 5));
|
||||||
|
|
||||||
Add(new Style(typeof(TabBar))
|
Add(new Style(typeof(TabBar))
|
||||||
{
|
{
|
||||||
|
@ -28,6 +28,7 @@ namespace Billing.Themes
|
|||||||
Add(TextColor, Color.FromRgb(0x33, 0x33, 0x33));
|
Add(TextColor, Color.FromRgb(0x33, 0x33, 0x33));
|
||||||
Add(SecondaryTextColor, Color.DimGray);
|
Add(SecondaryTextColor, Color.DimGray);
|
||||||
Add(RedColor, Color.FromRgb(211, 64, 85));
|
Add(RedColor, Color.FromRgb(211, 64, 85));
|
||||||
|
Add(RedColor, Color.FromRgb(64, 211, 85));
|
||||||
|
|
||||||
Add(new Style(typeof(TabBar))
|
Add(new Style(typeof(TabBar))
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using Billing.Languages;
|
using Billing.Languages;
|
||||||
using Billing.Models;
|
using Billing.Models;
|
||||||
|
using Billing.Themes;
|
||||||
|
using Billing.Views;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@ -20,18 +22,23 @@ namespace Billing.UI
|
|||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
return value;
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MoneyConverter : IValueConverter
|
public class MoneyConverter : IValueConverter
|
||||||
{
|
{
|
||||||
public bool MarkVisible { get; set; } = true;
|
public bool MarkVisible { get; set; } = true;
|
||||||
|
public bool Absolute { get; set; }
|
||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
if (value is decimal d)
|
if (value is decimal d)
|
||||||
{
|
{
|
||||||
|
if (Absolute)
|
||||||
|
{
|
||||||
|
d = Math.Abs(d);
|
||||||
|
}
|
||||||
var number = d.ToString("n2");
|
var number = d.ToString("n2");
|
||||||
if (MarkVisible)
|
if (MarkVisible)
|
||||||
{
|
{
|
||||||
@ -60,6 +67,46 @@ namespace Billing.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class BalanceColorConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
var resource = Application.Current.Resources;
|
||||||
|
if (value is decimal d)
|
||||||
|
{
|
||||||
|
if (d >= 0)
|
||||||
|
{
|
||||||
|
return resource[BaseTheme.GreenColor];
|
||||||
|
}
|
||||||
|
return resource[BaseTheme.RedColor];
|
||||||
|
}
|
||||||
|
return resource[BaseTheme.TextColor];
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UIBillConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is UIBill bill)
|
||||||
|
{
|
||||||
|
var time = bill.DateCreation.ToString("HH:mm");
|
||||||
|
return $"{time} ({bill.Wallet})";
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class NotNullConverter : IValueConverter
|
public class NotNullConverter : IValueConverter
|
||||||
{
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
@ -69,7 +116,7 @@ namespace Billing.UI
|
|||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
return value;
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +140,7 @@ namespace Billing.UI
|
|||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
return value;
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +198,7 @@ namespace Billing.UI
|
|||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
return value;
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Billing.Models;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.UI
|
namespace Billing.UI
|
||||||
@ -75,6 +79,51 @@ namespace Billing.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ModelExtensionHelper
|
||||||
|
{
|
||||||
|
public static List<T> FromStream<T>(Stream stream) where T : IModel, new()
|
||||||
|
{
|
||||||
|
XDocument doc = XDocument.Load(stream);
|
||||||
|
var root = doc.Root;
|
||||||
|
var list = new List<T>();
|
||||||
|
foreach (XElement ele in root.Elements("item"))
|
||||||
|
{
|
||||||
|
if (ele.Attribute("null")?.Value == "1")
|
||||||
|
{
|
||||||
|
list.Add(default);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
T value = new();
|
||||||
|
value.OnXmlDeserialize(ele);
|
||||||
|
list.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ToStream<T>(this IEnumerable<T> list, Stream stream) where T : IModel
|
||||||
|
{
|
||||||
|
XElement root = new("root");
|
||||||
|
foreach (var t in list)
|
||||||
|
{
|
||||||
|
XElement item = new("item");
|
||||||
|
if (t == null)
|
||||||
|
{
|
||||||
|
item.Add(new XAttribute("null", 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t.OnXmlSerialize(item);
|
||||||
|
}
|
||||||
|
root.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
XDocument doc = new(new XDeclaration("1.0", "utf-8", "yes"), root);
|
||||||
|
doc.Save(stream, SaveOptions.DisableFormatting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class Tap : IDisposable
|
public class Tap : IDisposable
|
||||||
{
|
{
|
||||||
private readonly static object sync = new();
|
private readonly static object sync = new();
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Linq;
|
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.UI
|
namespace Billing.UI
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ui:MoneyConverter x:Key="moneyConverter"/>
|
<ui:MoneyConverter x:Key="moneyConverter"/>
|
||||||
<ui:MoneyConverter x:Key="money2Converter" MarkVisible="False"/>
|
<ui:MoneyConverter x:Key="money2Converter" MarkVisible="False"/>
|
||||||
|
<ui:AccountCategoryConverter x:Key="categoryConverter"/>
|
||||||
<ui:IconConverter x:Key="iconConverter"/>
|
<ui:IconConverter x:Key="iconConverter"/>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
@ -43,11 +44,12 @@
|
|||||||
Text="{Binding Liability, Converter={StaticResource moneyConverter}}"/>
|
Text="{Binding Liability, Converter={StaticResource moneyConverter}}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ui:GroupStackLayout x:Name="groupLayout" ItemsSource="{Binding Accounts}">
|
<ui:GroupStackLayout x:Name="groupLayout" ItemsSource="{Binding Accounts}" Margin="0, 10, 0, 0">
|
||||||
<ui:GroupStackLayout.GroupHeaderTemplate>
|
<ui:GroupStackLayout.GroupHeaderTemplate>
|
||||||
<DataTemplate x:DataType="v:AccountGrouping">
|
<DataTemplate x:DataType="v:AccountGrouping">
|
||||||
<StackLayout Orientation="Horizontal" Padding="10, 0">
|
<StackLayout Orientation="Horizontal" Padding="10, 0">
|
||||||
<Label Text="{Binding Key}" TextColor="{DynamicResource SecondaryTextColor}"/>
|
<Label Text="{Binding Key, Converter={StaticResource categoryConverter}}"
|
||||||
|
TextColor="{DynamicResource SecondaryTextColor}"/>
|
||||||
<Label Text="{Binding Balance, Converter={StaticResource money2Converter}}"
|
<Label Text="{Binding Balance, Converter={StaticResource money2Converter}}"
|
||||||
Margin="10, 0" TextColor="{DynamicResource SecondaryTextColor}"/>
|
Margin="10, 0" TextColor="{DynamicResource SecondaryTextColor}"/>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
@ -55,7 +57,7 @@
|
|||||||
</ui:GroupStackLayout.GroupHeaderTemplate>
|
</ui:GroupStackLayout.GroupHeaderTemplate>
|
||||||
<ui:GroupStackLayout.ItemTemplate>
|
<ui:GroupStackLayout.ItemTemplate>
|
||||||
<DataTemplate x:DataType="m:Account">
|
<DataTemplate x:DataType="m:Account">
|
||||||
<StackLayout Orientation="Horizontal" Padding="20, 0, 10, 0" HeightRequest="44" Spacing="10">
|
<StackLayout Orientation="Horizontal" Padding="20, 0, 10, 0" Spacing="10">
|
||||||
<ui:TintImage Source="{Binding Icon, Converter={StaticResource iconConverter}}"
|
<ui:TintImage Source="{Binding Icon, Converter={StaticResource iconConverter}}"
|
||||||
WidthRequest="26" HeightRequest="20" VerticalOptions="Center"/>
|
WidthRequest="26" HeightRequest="20" VerticalOptions="Center"/>
|
||||||
<Label Text="{Binding Name}" TextColor="{DynamicResource TextColor}"
|
<Label Text="{Binding Name}" TextColor="{DynamicResource TextColor}"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Billing.Models;
|
using Billing.Models;
|
||||||
using Billing.UI;
|
using Billing.UI;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@ -20,17 +21,57 @@ namespace Billing.Views
|
|||||||
|
|
||||||
public Command AddAccount { get; }
|
public Command AddAccount { get; }
|
||||||
|
|
||||||
private readonly List<AccountGrouping> accounts;
|
private readonly List<AccountGrouping> accounts = new();
|
||||||
|
private bool initialized;
|
||||||
|
|
||||||
public AccountPage()
|
public AccountPage()
|
||||||
{
|
{
|
||||||
AddAccount = new Command(OnAddAccount);
|
AddAccount = new Command(OnAddAccount);
|
||||||
accounts = new List<AccountGrouping>();
|
|
||||||
SetValue(AccountsProperty, accounts);
|
SetValue(AccountsProperty, accounts);
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnAppearing()
|
||||||
|
{
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
initialized = true;
|
||||||
|
accounts.Clear();
|
||||||
|
foreach (var account in App.Accounts)
|
||||||
|
{
|
||||||
|
AddToAccountGroup(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
groupLayout.Refresh(accounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddToAccountGroup(Account account)
|
||||||
|
{
|
||||||
|
int maxId;
|
||||||
|
if (accounts.Count > 0)
|
||||||
|
{
|
||||||
|
maxId = accounts.Max(g => g.Max(a => a.Id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
maxId = -1;
|
||||||
|
}
|
||||||
|
account.Id = maxId + 1;
|
||||||
|
|
||||||
|
var group = accounts.FirstOrDefault(g => g.Key == account.Category);
|
||||||
|
if (group == null)
|
||||||
|
{
|
||||||
|
group = new AccountGrouping(account.Category, account.Balance) { account };
|
||||||
|
accounts.Add(group);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
group.Add(account);
|
||||||
|
group.Balance += account.Balance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async void OnAddAccount()
|
private async void OnAddAccount()
|
||||||
{
|
{
|
||||||
if (Tap.IsBusy)
|
if (Tap.IsBusy)
|
||||||
@ -47,28 +88,23 @@ namespace Billing.Views
|
|||||||
|
|
||||||
private void AccountChecked(object sender, AccountEventArgs e)
|
private void AccountChecked(object sender, AccountEventArgs e)
|
||||||
{
|
{
|
||||||
Helper.Debug(e.Account.ToString());
|
App.Accounts.Add(e.Account);
|
||||||
var group = accounts.FirstOrDefault(g => g.Key == e.Account.Category);
|
AddToAccountGroup(e.Account);
|
||||||
if (group == null)
|
|
||||||
{
|
|
||||||
group = new AccountGrouping(e.Account.Category)
|
|
||||||
{
|
|
||||||
e.Account
|
|
||||||
};
|
|
||||||
accounts.Add(group);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
group.Add(e.Account);
|
|
||||||
}
|
|
||||||
groupLayout.Refresh(accounts);
|
groupLayout.Refresh(accounts);
|
||||||
|
|
||||||
|
Task.Run(App.WriteAccounts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AccountGrouping : List<Account>, IGrouping<AccountCategory, Account>
|
public class AccountGrouping : List<Account>, IGrouping<AccountCategory, Account>
|
||||||
{
|
{
|
||||||
public AccountGrouping(AccountCategory key) : base() => Key = key;
|
|
||||||
public AccountCategory Key { get; }
|
public AccountCategory Key { get; }
|
||||||
public decimal Balance { get; set; }
|
public decimal Balance { get; set; }
|
||||||
|
|
||||||
|
public AccountGrouping(AccountCategory key, decimal balance) : base()
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Balance = balance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,15 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
|
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
xmlns:r="clr-namespace:Billing.Languages"
|
xmlns:r="clr-namespace:Billing.Languages"
|
||||||
xmlns:ui="clr-namespace:Billing.UI"
|
xmlns:ui="clr-namespace:Billing.UI"
|
||||||
xmlns:v="clr-namespace:Billing.Views"
|
xmlns:v="clr-namespace:Billing.Views"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
|
||||||
x:Class="Billing.Views.AddAccountPage"
|
x:Class="Billing.Views.AddAccountPage"
|
||||||
x:Name="addAccountPage"
|
x:Name="addAccountPage"
|
||||||
x:DataType="v:AddAccountPage"
|
x:DataType="v:AddAccountPage"
|
||||||
Title="{r:Text AddAccount}"
|
Title="{r:Text AddAccount}"
|
||||||
BindingContext="{x:Reference addAccountPage}"
|
BindingContext="{x:Reference addAccountPage}">
|
||||||
NavigationPage.BackButtonTitle="">
|
|
||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ui:AccountCategoryConverter x:Key="categoryConverter"/>
|
<ui:AccountCategoryConverter x:Key="categoryConverter"/>
|
||||||
@ -42,7 +41,7 @@
|
|||||||
</TableSection.Title>
|
</TableSection.Title>
|
||||||
<ui:OptionEntryCell Height="44" Icon="sackdollar.png" Keyboard="Numeric"
|
<ui:OptionEntryCell Height="44" Icon="sackdollar.png" Keyboard="Numeric"
|
||||||
Title="{r:Text Balance}"
|
Title="{r:Text Balance}"
|
||||||
Text="{Binding Balance, Mode=TwoWay}"
|
Text="{Binding Initial, Mode=TwoWay}"
|
||||||
Placeholder="{r:Text BalancePlaceholder}"/>
|
Placeholder="{r:Text BalancePlaceholder}"/>
|
||||||
<ui:OptionTextCell Height="44" Icon="yuan.png"
|
<ui:OptionTextCell Height="44" Icon="yuan.png"
|
||||||
Title="{r:Text Currency}"
|
Title="{r:Text Currency}"
|
||||||
|
@ -12,7 +12,7 @@ namespace Billing.Views
|
|||||||
private static readonly BindableProperty AccountNameProperty = BindableProperty.Create(nameof(AccountName), typeof(string), typeof(AddAccountPage));
|
private static readonly BindableProperty AccountNameProperty = BindableProperty.Create(nameof(AccountName), typeof(string), typeof(AddAccountPage));
|
||||||
private static readonly BindableProperty AccountIconProperty = BindableProperty.Create(nameof(AccountIcon), typeof(string), typeof(AddAccountPage));
|
private static readonly BindableProperty AccountIconProperty = BindableProperty.Create(nameof(AccountIcon), typeof(string), typeof(AddAccountPage));
|
||||||
private static readonly BindableProperty CategoryProperty = BindableProperty.Create(nameof(Category), typeof(AccountCategory), typeof(AddAccountPage));
|
private static readonly BindableProperty CategoryProperty = BindableProperty.Create(nameof(Category), typeof(AccountCategory), typeof(AddAccountPage));
|
||||||
private static readonly BindableProperty BalanceProperty = BindableProperty.Create(nameof(Balance), typeof(decimal), typeof(AddAccountPage));
|
private static readonly BindableProperty InitialProperty = BindableProperty.Create(nameof(Initial), typeof(string), typeof(AddAccountPage));
|
||||||
private static readonly BindableProperty MemoProperty = BindableProperty.Create(nameof(Memo), typeof(string), typeof(AddAccountPage));
|
private static readonly BindableProperty MemoProperty = BindableProperty.Create(nameof(Memo), typeof(string), typeof(AddAccountPage));
|
||||||
|
|
||||||
public string AccountName
|
public string AccountName
|
||||||
@ -30,10 +30,10 @@ namespace Billing.Views
|
|||||||
get => (AccountCategory)GetValue(CategoryProperty);
|
get => (AccountCategory)GetValue(CategoryProperty);
|
||||||
set => SetValue(CategoryProperty, value);
|
set => SetValue(CategoryProperty, value);
|
||||||
}
|
}
|
||||||
public decimal Balance
|
public string Initial
|
||||||
{
|
{
|
||||||
get => (decimal)GetValue(BalanceProperty);
|
get => (string)GetValue(InitialProperty);
|
||||||
set => SetValue(BalanceProperty, value);
|
set => SetValue(InitialProperty, value);
|
||||||
}
|
}
|
||||||
public string Memo
|
public string Memo
|
||||||
{
|
{
|
||||||
@ -54,6 +54,7 @@ namespace Billing.Views
|
|||||||
CheckAccount = new Command(OnCheckAccount);
|
CheckAccount = new Command(OnCheckAccount);
|
||||||
SelectIcon = new Command(OnSelectIcon);
|
SelectIcon = new Command(OnSelectIcon);
|
||||||
SelectCategory = new Command(OnSelectCategory);
|
SelectCategory = new Command(OnSelectCategory);
|
||||||
|
|
||||||
AccountIcon = BaseModel.ICON_DEFAULT;
|
AccountIcon = BaseModel.ICON_DEFAULT;
|
||||||
Category = AccountCategory.Cash;
|
Category = AccountCategory.Cash;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -61,13 +62,16 @@ namespace Billing.Views
|
|||||||
|
|
||||||
public AddAccountPage(Account account)
|
public AddAccountPage(Account account)
|
||||||
{
|
{
|
||||||
|
CheckAccount = new Command(OnCheckAccount);
|
||||||
|
SelectIcon = new Command(OnSelectIcon);
|
||||||
|
SelectCategory = new Command(OnSelectCategory);
|
||||||
|
|
||||||
this.account = account;
|
this.account = account;
|
||||||
AccountName = account.Name;
|
AccountName = account.Name;
|
||||||
AccountIcon = account.Icon;
|
AccountIcon = account.Icon;
|
||||||
Category = account.Category;
|
Category = account.Category;
|
||||||
Balance = account.Balance;
|
Initial = account.Initial.ToString();
|
||||||
Memo = account.Memo;
|
Memo = account.Memo;
|
||||||
CheckAccount = new Command(OnCheckAccount);
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +84,7 @@ namespace Billing.Views
|
|||||||
using (Tap.Start())
|
using (Tap.Start())
|
||||||
{
|
{
|
||||||
await Navigation.PopAsync();
|
await Navigation.PopAsync();
|
||||||
|
_ = decimal.TryParse(Initial, out decimal initial);
|
||||||
AccountChecked?.Invoke(this, new AccountEventArgs
|
AccountChecked?.Invoke(this, new AccountEventArgs
|
||||||
{
|
{
|
||||||
Account = new Account
|
Account = new Account
|
||||||
@ -88,7 +93,8 @@ namespace Billing.Views
|
|||||||
Name = AccountName,
|
Name = AccountName,
|
||||||
Icon = AccountIcon,
|
Icon = AccountIcon,
|
||||||
Category = Category,
|
Category = Category,
|
||||||
Balance = Balance,
|
Initial = initial,
|
||||||
|
Balance = initial,
|
||||||
Memo = Memo
|
Memo = Memo
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,15 +1,52 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
|
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
xmlns:r="clr-namespace:Billing.Languages"
|
||||||
xmlns:ui="clr-namespace:Billing.UI"
|
xmlns:ui="clr-namespace:Billing.UI"
|
||||||
xmlns:v="clr-namespace:Billing.Views"
|
xmlns:v="clr-namespace:Billing.Views"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
|
||||||
x:Class="Billing.Views.AddBillPage"
|
x:Class="Billing.Views.AddBillPage"
|
||||||
|
x:Name="billPage"
|
||||||
x:DataType="v:AddBillPage"
|
x:DataType="v:AddBillPage"
|
||||||
Title="Add Billing">
|
BindingContext="{x:Reference billPage}">
|
||||||
|
|
||||||
<StackLayout>
|
<ContentPage.ToolbarItems>
|
||||||
<Label Text="Add a billing here..."
|
<ToolbarItem Order="Primary" IconImageSource="check.png" Command="{Binding CheckBill}"/>
|
||||||
VerticalOptions="CenterAndExpand"
|
</ContentPage.ToolbarItems>
|
||||||
HorizontalOptions="CenterAndExpand" />
|
|
||||||
</StackLayout>
|
<ContentPage.Content>
|
||||||
|
<TableView Intent="Settings" HasUnevenRows="True">
|
||||||
|
<TableSection Title=" ">
|
||||||
|
<ui:OptionEditorCell Height="120" Icon="pencil.png" FontSize="20" Keyboard="Text"
|
||||||
|
Title="{r:Text Account}"
|
||||||
|
Text="{Binding AccountName, Mode=TwoWay}"
|
||||||
|
Placeholder="0.00"/>
|
||||||
|
</TableSection>
|
||||||
|
<TableSection>
|
||||||
|
<TableSection.Title>
|
||||||
|
<OnPlatform x:TypeArguments="x:String" Android=" "/>
|
||||||
|
</TableSection.Title>
|
||||||
|
<ui:OptionSelectCell Height="44" Icon="project.png"
|
||||||
|
Title="{r:Text Category}"
|
||||||
|
Detail="{Binding Category, Converter={StaticResource categoryConverter}}"
|
||||||
|
Command="{Binding SelectCategory}"/>
|
||||||
|
<ui:OptionSelectCell Height="44" Icon="project.png"
|
||||||
|
Title="{r:Text Account}"
|
||||||
|
Detail="{Binding Category, Converter={StaticResource categoryConverter}}"
|
||||||
|
Command="{Binding SelectCategory}"/>
|
||||||
|
<ui:OptionSelectCell Height="44" Icon="project.png"
|
||||||
|
Title="{r:Text CreatedTime}"
|
||||||
|
Detail="{Binding Category, Converter={StaticResource categoryConverter}}"
|
||||||
|
Command="{Binding SelectCategory}"/>
|
||||||
|
</TableSection>
|
||||||
|
<TableSection>
|
||||||
|
<TableSection.Title>
|
||||||
|
<OnPlatform x:TypeArguments="x:String" Android=" "/>
|
||||||
|
</TableSection.Title>
|
||||||
|
<ui:OptionEditorCell Height="120" Icon="note.png" Keyboard="Plain"
|
||||||
|
Title="{r:Text Memo}"
|
||||||
|
Text="{Binding Memo, Mode=TwoWay}"
|
||||||
|
Placeholder="{r:Text MemoPlaceholder}"/>
|
||||||
|
</TableSection>
|
||||||
|
</TableView>
|
||||||
|
</ContentPage.Content>
|
||||||
</ui:BillingPage>
|
</ui:BillingPage>
|
@ -1,12 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using Billing.Languages;
|
||||||
|
using Billing.Models;
|
||||||
using Billing.UI;
|
using Billing.UI;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.Views
|
namespace Billing.Views
|
||||||
{
|
{
|
||||||
public partial class AddBillPage : BillingPage
|
public partial class AddBillPage : BillingPage
|
||||||
{
|
{
|
||||||
public AddBillPage()
|
public Command CheckBill { get; }
|
||||||
|
|
||||||
|
private readonly Bill bill;
|
||||||
|
private readonly DateTime createDate;
|
||||||
|
|
||||||
|
public AddBillPage(DateTime date)
|
||||||
{
|
{
|
||||||
|
createDate = date;
|
||||||
|
CheckBill = new Command(OnCheckBill);
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
Title = Resource.AddBill;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddBillPage(Bill bill)
|
||||||
|
{
|
||||||
|
this.bill = bill;
|
||||||
|
CheckBill = new Command(OnCheckBill);
|
||||||
|
InitializeComponent();
|
||||||
|
Title = Resource.EditBill;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCheckBill()
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,6 +12,10 @@
|
|||||||
|
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ui:TitleDateConverter x:Key="titleDateConverter"/>
|
<ui:TitleDateConverter x:Key="titleDateConverter"/>
|
||||||
|
<ui:MoneyConverter x:Key="moneyConverter" MarkVisible="False" Absolute="True"/>
|
||||||
|
<ui:BalanceColorConverter x:Key="colorConverter"/>
|
||||||
|
<ui:UIBillConverter x:Key="billConverter"/>
|
||||||
|
<ui:IconConverter x:Key="iconConverter"/>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<Shell.TitleView>
|
<Shell.TitleView>
|
||||||
@ -26,27 +30,44 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Shell.TitleView>
|
</Shell.TitleView>
|
||||||
|
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto, Auto, *">
|
||||||
<ui:BillingDate x:Name="billingDate" SelectedDate="{Binding SelectedDate}" DateSelected="OnDateSelected"/>
|
<ui:BillingDate x:Name="billingDate" SelectedDate="{Binding SelectedDate}" DateSelected="OnDateSelected"/>
|
||||||
<ScrollView Grid.Row="1">
|
<Grid Grid.Row="1" Padding="8" ColumnSpacing="8" ColumnDefinitions="Auto, *, Auto"
|
||||||
<Grid Padding="8" ColumnSpacing="8" ColumnDefinitions="Auto, *, Auto"
|
BackgroundColor="{DynamicResource PromptBackgroundColor}">
|
||||||
BackgroundColor="{DynamicResource PromptBackgroundColor}"
|
<ui:TintImage Source="bars.png" WidthRequest="23" HeightRequest="23"/>
|
||||||
VerticalOptions="Start">
|
<Label Grid.Column="1" Text="{r:Text NoRecords}" TextColor="{DynamicResource TextColor}"
|
||||||
<ui:TintImage Source="bars.png" WidthRequest="23" HeightRequest="23"/>
|
VerticalOptions="Center"/>
|
||||||
<Label Grid.Column="1" Text="{r:Text NoRecords}" TextColor="{DynamicResource TextColor}"
|
<StackLayout Grid.Column="2" Orientation="Horizontal" Spacing="6">
|
||||||
|
<StackLayout.GestureRecognizers>
|
||||||
|
<TapGestureRecognizer Command="{Binding AddBilling}"/>
|
||||||
|
</StackLayout.GestureRecognizers>
|
||||||
|
<Label Text="{r:Text TapToMemo}" TextColor="{DynamicResource PrimaryColor}"
|
||||||
VerticalOptions="Center"/>
|
VerticalOptions="Center"/>
|
||||||
<StackLayout Grid.Column="2" Orientation="Horizontal" Spacing="6">
|
<ui:TintImage Source="right.png" WidthRequest="24" HeightRequest="24"/>
|
||||||
<StackLayout.GestureRecognizers>
|
</StackLayout>
|
||||||
<TapGestureRecognizer Command="{Binding AddBilling}"/>
|
</Grid>
|
||||||
</StackLayout.GestureRecognizers>
|
<!-- bill list -->
|
||||||
<Label Text="{r:Text TapToMemo}" TextColor="{DynamicResource PrimaryColor}"
|
<ScrollView Grid.Row="2">
|
||||||
VerticalOptions="Center"/>
|
<ui:GroupStackLayout x:Name="billsLayout" ItemsSource="{Binding Bills}" Margin="0, 10, 0, 0">
|
||||||
<!--<Label Style="{DynamicResource IconLightStyle}"
|
<ui:GroupStackLayout.ItemTemplate>
|
||||||
Text="{x:Static local:Definition.IconRight}"
|
<DataTemplate x:DataType="v:UIBill">
|
||||||
TextColor="{DynamicResource TabBarUnselectedColor}"/>-->
|
<Grid Padding="20, 0, 10, 0" ColumnSpacing="10"
|
||||||
<ui:TintImage Source="right.png" WidthRequest="24" HeightRequest="24"/>
|
ColumnDefinitions="Auto, *, Auto" RowDefinitions="Auto, Auto">
|
||||||
</StackLayout>
|
<ui:TintImage Source="{Binding Icon, Converter={StaticResource iconConverter}}"
|
||||||
</Grid>
|
WidthRequest="26" HeightRequest="20" VerticalOptions="Center"/>
|
||||||
|
<Label Grid.Column="1" Text="{Binding Name}" TextColor="{DynamicResource TextColor}"
|
||||||
|
VerticalOptions="Center"
|
||||||
|
FontSize="Default" FontAttributes="Bold"/>
|
||||||
|
<Label Grid.Column="2" Text="{Binding Amount, Converter={StaticResource moneyConverter}}"
|
||||||
|
TextColor="{Binding Amount, Converter={StaticResource colorConverter}}"
|
||||||
|
VerticalOptions="Center"/>
|
||||||
|
<Label Grid.Row="1" Grid.Column="1" Text="{Binding ., Converter={StaticResource billConverter}}"
|
||||||
|
FontSize="Small"
|
||||||
|
TextColor="{DynamicResource SecondaryTextColor}"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ui:GroupStackLayout.ItemTemplate>
|
||||||
|
</ui:GroupStackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<!--<ui:CircleButton Grid.Row="1" VerticalOptions="End" HorizontalOptions="End"
|
<!--<ui:CircleButton Grid.Row="1" VerticalOptions="End" HorizontalOptions="End"
|
||||||
Margin="20" Padding="0"
|
Margin="20" Padding="0"
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
using Billing.Models;
|
||||||
using Billing.UI;
|
using Billing.UI;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Billing.Views
|
namespace Billing.Views
|
||||||
@ -7,12 +10,18 @@ namespace Billing.Views
|
|||||||
public partial class BillPage : BillingPage
|
public partial class BillPage : BillingPage
|
||||||
{
|
{
|
||||||
private static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillPage));
|
private static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillPage));
|
||||||
|
private static readonly BindableProperty BillsProperty = BindableProperty.Create(nameof(Bills), typeof(List<UIBill>), typeof(BillPage));
|
||||||
|
|
||||||
public DateTime SelectedDate
|
public DateTime SelectedDate
|
||||||
{
|
{
|
||||||
get => (DateTime)GetValue(SelectedDateProperty);
|
get => (DateTime)GetValue(SelectedDateProperty);
|
||||||
set => SetValue(SelectedDateProperty, value);
|
set => SetValue(SelectedDateProperty, value);
|
||||||
}
|
}
|
||||||
|
public List<UIBill> Bills
|
||||||
|
{
|
||||||
|
get => (List<UIBill>)GetValue(BillsProperty);
|
||||||
|
set => SetValue(BillsProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public Command AddBilling { get; }
|
public Command AddBilling { get; }
|
||||||
|
|
||||||
@ -29,7 +38,18 @@ namespace Billing.Views
|
|||||||
{
|
{
|
||||||
SelectedDate = e.Date;
|
SelectedDate = e.Date;
|
||||||
|
|
||||||
// TODO: while selecting date
|
var bills = App.Bills.Where(b =>
|
||||||
|
b.CreateTime.Year == e.Date.Year &&
|
||||||
|
b.CreateTime.Month == e.Date.Month &&
|
||||||
|
b.CreateTime.Day == e.Date.Day);
|
||||||
|
Bills = new List<UIBill>(bills.Select(b => new UIBill(b)
|
||||||
|
{
|
||||||
|
Icon = App.Categories.FirstOrDefault(c => c.Id == b.CategoryId)?.Icon ?? BaseModel.ICON_DEFAULT,
|
||||||
|
Name = b.Name,
|
||||||
|
DateCreation = b.CreateTime,
|
||||||
|
Amount = b.Amount,
|
||||||
|
Wallet = App.Accounts.FirstOrDefault(a => a.Id == b.WalletId)?.Name
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTitleDateLongPressed(object sender, EventArgs e)
|
private void OnTitleDateLongPressed(object sender, EventArgs e)
|
||||||
@ -45,8 +65,51 @@ namespace Billing.Views
|
|||||||
}
|
}
|
||||||
using (Tap.Start())
|
using (Tap.Start())
|
||||||
{
|
{
|
||||||
await Navigation.PushAsync(new AddBillPage());
|
var page = new AddBillPage(SelectedDate);
|
||||||
|
await Navigation.PushAsync(page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class UIBill : BindableObject
|
||||||
|
{
|
||||||
|
public static readonly BindableProperty IconProperty = BindableProperty.Create(nameof(Icon), typeof(string), typeof(UIBill));
|
||||||
|
public static readonly BindableProperty NameProperty = BindableProperty.Create(nameof(Name), typeof(string), typeof(UIBill));
|
||||||
|
public static readonly BindableProperty DateCreationProperty = BindableProperty.Create(nameof(DateCreation), typeof(DateTime), typeof(UIBill));
|
||||||
|
public static readonly BindableProperty AmountProperty = BindableProperty.Create(nameof(Amount), typeof(decimal), typeof(UIBill));
|
||||||
|
public static readonly BindableProperty WalletProperty = BindableProperty.Create(nameof(Wallet), typeof(string), typeof(UIBill));
|
||||||
|
|
||||||
|
public string Icon
|
||||||
|
{
|
||||||
|
get => (string)GetValue(IconProperty);
|
||||||
|
set => SetValue(IconProperty, value);
|
||||||
|
}
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => (string)GetValue(NameProperty);
|
||||||
|
set => SetValue(NameProperty, value);
|
||||||
|
}
|
||||||
|
public DateTime DateCreation
|
||||||
|
{
|
||||||
|
get => (DateTime)GetValue(DateCreationProperty);
|
||||||
|
set => SetValue(DateCreationProperty, value);
|
||||||
|
}
|
||||||
|
public decimal Amount
|
||||||
|
{
|
||||||
|
get => (decimal)GetValue(AmountProperty);
|
||||||
|
set => SetValue(AmountProperty, value);
|
||||||
|
}
|
||||||
|
public string Wallet
|
||||||
|
{
|
||||||
|
get => (string)GetValue(WalletProperty);
|
||||||
|
set => SetValue(WalletProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bill Bill { get; }
|
||||||
|
|
||||||
|
public UIBill(Bill bill)
|
||||||
|
{
|
||||||
|
Bill = bill;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -38,7 +38,9 @@ namespace Billing.Views
|
|||||||
var source = new List<BillingIcon>
|
var source = new List<BillingIcon>
|
||||||
{
|
{
|
||||||
new() { Icon = BaseModel.ICON_DEFAULT },
|
new() { Icon = BaseModel.ICON_DEFAULT },
|
||||||
new() { Icon = "wallet" }
|
new() { Icon = "wallet" },
|
||||||
|
new() { Icon = "creditcard" },
|
||||||
|
new() { Icon = "debitcard" }
|
||||||
};
|
};
|
||||||
source.AddRange(IconConverter.IconPreset.Select(icon => new BillingIcon { Icon = $"#brand#{icon.Key}" }));
|
source.AddRange(IconConverter.IconPreset.Select(icon => new BillingIcon { Icon = $"#brand#{icon.Key}" }));
|
||||||
foreach (var icon in source)
|
foreach (var icon in source)
|
||||||
|
@ -35,7 +35,7 @@ namespace Billing.iOS.Renderers
|
|||||||
|
|
||||||
private void OnLongPressed(UILongPressGestureRecognizer e)
|
private void OnLongPressed(UILongPressGestureRecognizer e)
|
||||||
{
|
{
|
||||||
if (Element is LongPressButton button)
|
if (e.State == UIGestureRecognizerState.Began && Element is LongPressButton button)
|
||||||
{
|
{
|
||||||
button.TriggerLongPress();
|
button.TriggerLongPress();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user