Files
29WxOrder/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceViewModel.cs
T
2026-09-21 09:49:26 +08:00

900 lines
23 KiB
C#

using _29WxOrderWpf.BaseWindows;
using _29WxOrderWpf.CefHelper;
using _29WxOrderWpf.Commands;
using _29WxOrderWpf.Common;
using _29WxOrderWpf.Ctrls.Statistics;
using AllBase;
using AllBase.Helper;
using AllBase.Model.DBModel;
using AllBase.Model.Result;
using AllBase.Util;
using CefSharp;
using CefSharp.Handler;
using CefSharp.Wpf;
using MAX.Core.Helpers;
using NaiBa.Util.Extension;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
using Work._29Tools.CefTools;
using Work.BaseForms;
using WPFCommon.Converters;
using WPFCommon.Helpers;
using WPFCommon.Models;
using WxOrder.Framework.Helpers;
namespace _29WxOrderWpf.Ctrls.PDDInvoice.ViewModels;
/// <summary>
/// 拼多多发票助手 视图模型
/// </summary>
public partial class PDDInvoiceViewModel : BindableBaseEx
{
private const string ConfigPath = "./invoice.config";
private List<OrderInvoiceInfo> OrderInvoiceInfoList = new();
private readonly HashSet<string> _orderSns = new();
private string _searchText = "";
private PddBrowserOrderInvoiceViewModel _selectedOrder;
private bool _isInvoiceDialogOpen;
private bool _isInvoiceDetailOpen;
private string _invoiceTitle = "";
private string _taxNo = "";
private CancellationTokenSource _cancellationTokenSource;
private InvoiceConfig Config { get; set; }
/// <summary> 订单列表 </summary>
public ObservableCollection<PddBrowserOrderInvoiceViewModel> Orders { get; }
public ICollectionView OrdersView { get; }
public PDDInvoiceViewModel()
{
_cancellationTokenSource = new CancellationTokenSource();
Config = NaiBa.Util.Helper.ConfigHelper.LoadConfig<InvoiceConfig>(ConfigPath);
Orders = new ObservableCollection<PddBrowserOrderInvoiceViewModel>();
Orders.CollectionChanged += Orders_CollectionChanged;
OrdersView = CollectionViewSource.GetDefaultView(Orders);
OrdersView.Filter = FilterPredicate;
OrdersView.SortDescriptions.Add(
new SortDescription(nameof(PddBrowserOrderInvoiceViewModel.order_time), ListSortDirection.Ascending));
BuyerLoginList = GlobalConfig.PDDBuyerLoginList;
//Orders = new ObservableCollection<PDDInvoiceOrderModel>(_allOrders);
InitBrowser();
if (Config == null) Config = new InvoiceConfig();
EndTime = DateTime.Now;
StartTime = EndTime.AddMonths(-1);
if (EnvironmentHelper.IsTest)
{
Config.Title = "嵩县雷神商贸有限责任公司";
Config.TaxNo = "91410325MADQERT68E";
Config.MaxAmount = 99;
_ = InitSampleData();
}
}
private bool FilterPredicate(object obj)
{
if (obj is not PddBrowserOrderInvoiceViewModel order) return false;
// 发票状态过滤
bool? temp = InvoiceStatus == 0 ? null : InvoiceStatus == 2;
if (temp.HasValue && order.IsInvoiced != temp.Value)
return false;
// 关键字过滤
var keyword = SearchText?.Trim();
if (!IsKeywordMatch(order, keyword))
return false;
if (order.OrderTime < StartTime || EndTime < order.OrderTime) return false;
return true;
}
partial void InitBrowser();
/// <summary> 列表增删时订阅/退订订单勾选状态,用于刷新表头全选状态 </summary>
private void Orders_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (PddBrowserOrderInvoiceViewModel item in e.NewItems)
{
item.PropertyChanged += Order_PropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (PddBrowserOrderInvoiceViewModel item in e.OldItems)
{
item.PropertyChanged -= Order_PropertyChanged;
}
}
RefreshSelectAllState();
}
private void Order_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(PddBrowserOrderInvoiceViewModel.IsSelected))
{
RefreshSelectAllState();
}
}
public ObservableCollectionThreadSafe<BuyerLoginInfo> BuyerLoginList { get; set; } = new ObservableCollectionThreadSafe<BuyerLoginInfo>();
private void InvoiceStatusChanged()
{
}
public BuyerLoginInfo SelectedBuyer
{
get => GetProperty(() => SelectedBuyer);
set => SetProperty(() => SelectedBuyer, value, SelectedBuyerChanged);
}
private void SelectedBuyerChanged()
{
WPFCEFBrowser.SetCookieAsync(SelectedBuyer.cookie, Browser.GetCookieManager()).GetAwaiter();
WPFCEFBrowser.SetCookieAsync(SelectedBuyer.cookie, BrowserDetail.GetCookieManager()).GetAwaiter();
}
/// <summary> 搜索关键字:订单号 / 商品名称 / 买家 </summary>
public string SearchText
{
get => _searchText;
set => SetProperty(ref _searchText, value, nameof(SearchText));
}
public int InvoiceStatus
{
get => GetValue<int>();
set => SetValue(value, InvoiceStatusChanged);
}
public DateTime StartTime
{
get => GetValue<DateTime>();
set => SetValue(value);
}
public DateTime EndTime
{
get => GetValue<DateTime>();
set => SetValue(value);
}
/// <summary> 开票弹窗是否打开 </summary>
public bool IsOrderListDialogOpen
{
get => GetValue<bool>();
set
{
SetValue(value);
if (value)
{
ShowOrderBrowser();
}
else
{
HidenOrderBrowser();
}
}
}
/// <summary> 发票详情弹窗是否打开 </summary>
public bool IsInvoiceDetailOpen
{
get => _isInvoiceDetailOpen;
set => SetProperty(ref _isInvoiceDetailOpen, value, nameof(IsInvoiceDetailOpen));
}
/// <summary> 当前操作的订单 </summary>
public PddBrowserOrderInvoiceViewModel SelectedOrder
{
get => _selectedOrder;
set => SetProperty(ref _selectedOrder, value, nameof(SelectedOrder));
}
///// <summary> 发票抬头 </summary>
//public string InvoiceTitle
//{
// get => _invoiceTitle;
// set => SetProperty(ref _invoiceTitle, value, nameof(InvoiceTitle));
//}
///// <summary> 税号 </summary>
//public string TaxNo
//{
// get => _taxNo;
// set => SetProperty(ref _taxNo, value, nameof(TaxNo));
//}
/// <summary>
/// 开始开票总金额
/// </summary>
public decimal AmountTotal
{
get => GetValue<decimal>();
set => SetValue(value);
}
///// <summary>
///// 最大开票金额
///// </summary>
//public decimal MaxAmount
//{
// get => GetValue<decimal>();
// set => SetValue(value);
//}
/// <summary>
/// 已开票金额
/// </summary>
public decimal InvoicedAmountTotal
{
get => GetValue<decimal>();
set => SetValue(value);
}
/// <summary>
/// 未开票金额
/// </summary>
public decimal UninvoicedAmountTotal
{
get => GetValue<decimal>();
set => SetValue(value);
}
public bool IsLoadImage
{
get => GetValue<bool>();
set => SetValue(value);
}
public ICommand SearchCommand => new DelegateCommand(Search);
public ICommand ResetCommand => new DelegateCommand(ResetSearch);
public ICommand OpenInvoiceCommand => new DelegateCommand<PddBrowserOrderInvoiceViewModel>(OpenInvoice);
public ICommand ShowInvoiceCommand => new DelegateCommand<PddBrowserOrderInvoiceViewModel>(ShowInvoice);
public ICommand ViewOrderCommand => new DelegateCommand<PddBrowserOrderInvoiceViewModel>(ViewOrder);
public ICommand ReadOrderCommand => new AsyncDelegateCommand(ReadOrder);
private async Task ReadOrder()
{
if (SelectedBuyer == null)
{
BaseMsgWindow.ShowError("未填选择买手号");
return;
}
Debug.WriteLine("开始读单");
BottomMsg = $"开始读取订单";
StartProgress();
try
{
if (!IsReadOrder)
{
_orderSns.Clear();
Orders.Clear();
OrderInvoiceInfoList.Clear();
string path = string.Format(inviceDataPath, SelectedBuyer.buyer_id);
if (File.Exists(NaiBa.Util.Helper.ConfigHelper.GetConfigUserPath(path)))
{
var ret = BaseMsgWindow.ShowConfirm("检测到已有历史数据是否加载?").DialogResult;
if (ret == true)
{
var data = NaiBa.Util.Helper.ConfigHelper.LoadConfig<List<OrderInvoiceInfo>>(path);
AddOrders(data.Select(a => new PddBrowserOrderInvoiceViewModel(a)));
BottomMsg = $"已加载历史数据";
return;
}
else if (ret == null)
{
return;
}
}
Browser.Load("https://mobile.yangkeduo.com/orders.html");
await Task.Delay(Timeout.Infinite, _cancellationTokenSource.Token);
}
}
catch (TaskCanceledException)
{
Debug.WriteLine("停止读单");
}
//ShowOrderBrowser();
}
public ICommand StopReadOrderCommand => new AsyncDelegateCommand(StopReadOrder);
private async Task StopReadOrder()
{
StopAutoScroll();
CompleteProgress();
}
#region 勾选 / 全选 / 反选
public int SelectedCount
{
get => GetValue<int>();
set => SetValue(value);
}
private bool? _isSelectAll;
private bool _isUpdatingSelectAll;
/// <summary> 表头全选状态(三态:null 表示部分选中) </summary>
public bool? IsSelectAll
{
get => _isSelectAll;
set
{
if (_isSelectAll == value) return;
_isSelectAll = value;
RaisePropertyChanged(nameof(IsSelectAll));
if (_isUpdatingSelectAll) return;
if (value == true)
{
foreach (var order in Orders)
{
if (order.IsInvoiced == false)
{
order.IsSelected = true;
}
}
}
else if (value == false)
{
foreach (var order in Orders) order.IsSelected = false;
}
RefreshSelectAllState();
}
}
public ICommand InvertSelectionCommand => new DelegateCommand(InvertSelection);
/// <summary> 反选当前可见列表 </summary>
private void InvertSelection()
{
foreach (var order in Orders) order.IsSelected = !order.IsSelected;
RefreshSelectAllState();
}
/// <summary> 根据当前可见列表的勾选情况刷新表头全选状态 </summary>
private void RefreshSelectAllState()
{
_isUpdatingSelectAll = true;
try
{
if (Orders.Count == 0)
{
IsSelectAll = false;
return;
}
int selected = Orders.Count(o => o.IsSelected);
IsSelectAll = selected == 0 ? false : selected == Orders.Count ? true : (bool?)null;
SelectedCount = selected;
}
finally
{
_isUpdatingSelectAll = false;
}
}
#endregion
#region 批量开票
private bool _isBatchInvoicing;
/// <summary> 是否正在批量开票 </summary>
public bool IsBatchInvoicing
{
get => _isBatchInvoicing;
set => SetProperty(ref _isBatchInvoicing, value, nameof(IsBatchInvoicing));
}
/// <summary> 当前正在批量处理的订单,页面脚本执行完成后通过它回传结果 </summary>
private TaskCompletionSource<bool> _currentInvoiceTcs;
public ICommand BatchInvoiceCommand => new AsyncDelegateCommand(BatchInvoice);
/// <summary>
/// 批量开票:统计勾选的待开票订单 → 确认 → 后台逐单自动开票
/// </summary>
private async Task BatchInvoice()
{
if (IsBatchInvoicing) return;
if (string.IsNullOrEmpty(Config.Title) || string.IsNullOrEmpty(Config.TaxNo))
{
BaseMsgWindow.ShowError("未填写公司或税号");
return;
}
var targets = OrdersView.Cast<PddBrowserOrderInvoiceViewModel>().Where(o => o.IsSelected && !o.IsInvoiced).ToList();
if (targets.Count == 0)
{
BaseMsgWindow.ShowError("请先勾选需要开票的订单");
return;
}
if (Config.MaxAmount <= 0)
{
BaseMsgWindow.ShowError("请填写开票总金额");
return;
}
var confirm = BaseMsgWindow.ShowConfirm(
$"已勾选 {targets.Count} 个待开票订单,确定开始批量开票吗?\n请确认发票抬头与税号无误。\n开票总金额:{Config.MaxAmount}",
"批量开票");
if (confirm.DialogResult != true) return;
//Config.Title = InvoiceTitle;
//Config.TaxNo = TaxNo;
//Config.MaxAmount = MaxAmount;
//NaiBa.Util.Helper.ConfigHelper.SaveConfig(ConfigPath, Config);
await BatchInvoiceAsync(targets);
}
public ICommand StopBatchInvoiceCommand => new AsyncDelegateCommand(StopBatchInvoice);
private async Task StopBatchInvoice()
{
IsBatchInvoicing = false;
}
public ICommand ShowSettingDialogCommand => new DelegateCommand(ShowSettingDialog);
private void ShowSettingDialog()
{
var view = new PDDInvoiceDialog();
new DialogWindow
{
Title = "设置",
Content = view,
}.ShowDialog();
Config = view.Config;
}
public event EventHandler<int> BatchInvoiceChanged;
/// <summary>
/// 后台批量开票:逐单加载开票页并自动填写提交,底部进度条实时更新,成功后更新列表状态
/// </summary>
private async Task BatchInvoiceAsync(List<PddBrowserOrderInvoiceViewModel> targets)
{
IsBatchInvoicing = true;
AmountTotal = 0;
int success = 0, fail = 0;
try
{
ProgressMaximum = targets.Count;
ProgressValue = 0;
BottomMsg = $"正在批量开票:0/{targets.Count}";
for (int i = 0; i < targets.Count; i++)
{
if (IsBatchInvoicing == false)
{
BottomMsg = "已取消批量开票";
return;
}
if (AmountTotal >= Config.MaxAmount)
{
return;
}
var order = targets[i];
BottomMsg = $"正在批量开票:{i}/{targets.Count},当前订单:{order.order_sn}";
ProgressValue = i;
SelectedOrder = order;
BatchInvoiceChanged?.Invoke(this, OrdersView.Cast<PddBrowserOrderInvoiceViewModel>().ToList().IndexOf(order));
bool ok = false;
if (EnvironmentHelper.IsTest)
{
ok = true;
await Task.Delay(1000);
}
else
{
_currentInvoiceTcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
BrowserDetail.Load($"https://mobile.yangkeduo.com/transac_return_compensation.html?_t_timestamp=transac_modify_invoice&order_sn={order.order_sn}");
// 等待页面脚本执行完成(最长 60 秒)
var done = await Task.WhenAny(_currentInvoiceTcs.Task, Task.Delay(TimeSpan.FromSeconds(60)));
ok = done == _currentInvoiceTcs.Task && _currentInvoiceTcs.Task.Result;
}
if (ok)
{
order.IsInvoiced = true;
order.IsSelected = false;
success++;
AmountTotal += order.order_amount / 100m;
}
else
{
fail++;
}
ProgressValue = success + fail;
BottomMsg = $"正在批量开票:{i}/{targets.Count},当前订单:{order.order_sn}。总金额:{AmountTotal.ToString("N2")}";
BottomRMsg = $"批量开票状态:成功 {success} 个,失败 {fail} 个。总金额:{AmountTotal.ToString("N2")}";
StatisticsInvoiceAmount();
await Task.Delay(1000);
}
}
catch (Exception ex)
{
fail = targets.Count - success;
BaseMsgWindow.ShowError("批量开票异常:" + ex.Message);
}
finally
{
_currentInvoiceTcs = null;
ProgressMaximum = targets.Count;
ProgressValue = targets.Count;
IsBatchInvoicing = false;
BaseMsgWindow.ShowInfo(BottomMsg);
}
}
/// <summary>
/// 统计发票金额
/// </summary>
private void StatisticsInvoiceAmount()
{
InvoicedAmountTotal = OrdersView.Cast<PddBrowserOrderInvoiceViewModel>().Where(a => a.IsInvoiced).Sum(a => a.order_amount) / 100m;
UninvoicedAmountTotal = OrdersView.Cast<PddBrowserOrderInvoiceViewModel>().Where(a => a.IsInvoiced == false).Sum(a => a.order_amount) / 100m;
}
#endregion
/// <summary> 按 订单号 / 商品名称 过滤订单 </summary>
private void Search()
{
OrdersView.Refresh();
StatisticsInvoiceAmount();
//var keyword = SearchText?.Trim();
//bool? _tempIsInvoiced = InvoiceStatus == 0 ? null : InvoiceStatus == 2;
//foreach (var order in Orders)
//{
// if (_tempIsInvoiced.HasValue)
// {
// if (order.IsInvoiced != _tempIsInvoiced.Value) continue;
// }
// if (!IsKeywordMatch(order, keyword))
// {
// continue;
// }
// Orders.Add(order);
//}
}
private static bool IsKeywordMatch(PddBrowserOrderInvoiceViewModel order, string keyword)
{
return string.IsNullOrEmpty(keyword)
|| (!string.IsNullOrEmpty(order.order_sn) && order.order_sn.Contains(keyword))
|| (!string.IsNullOrEmpty(order.GoodsName) && order.GoodsName.Contains(keyword));
}
private void ResetSearch()
{
SearchText = "";
InvoiceStatus = 0;
EndTime = DateTime.Now;
StartTime = EndTime.AddMonths(-1);
Search();
}
/// <summary>
/// 申请开票
/// </summary>
/// <param name="order"></param>
private void OpenInvoice(PddBrowserOrderInvoiceViewModel order)
{
SelectedOrder = order;
if (string.IsNullOrEmpty(Config.Title) || string.IsNullOrEmpty(Config.TaxNo))
{
BaseMsgWindow.ShowError("未填写公司或税号");
return;
}
BrowserDetail.Load($"https://mobile.yangkeduo.com/transac_return_compensation.html?_t_timestamp=transac_modify_invoice&order_sn={order.order_sn}");
ShowOrderBrowserDetail();
}
private void ShowInvoice(PddBrowserOrderInvoiceViewModel order)
{
SelectedOrder = order;
BrowserDetail.Load($"https://mobile.yangkeduo.com/transac_return_compensation.html?_t_timestamp=transac_invoice&order_sn={order.order_sn}");
ShowOrderBrowserDetail();
}
private void ViewOrder(PddBrowserOrderInvoiceViewModel order)
{
SelectedOrder = order;
BrowserDetail.Load($"https://mobile.yangkeduo.com/{order.order_link_url}");
ShowOrderBrowserDetail();
//MessageBox.Show($"查看订单:{order.OrderNo}");
}
#region 数据解析
private void parserData(string data)
{
List<PddBrowserOrderInvoiceViewModel> list = JsonHelper.DeserializeJsonToList<PddBrowserOrderInvoiceViewModel>(JsonHelper.JObjectParse(data)["orders"].ParseToString());
if (list.Count == 0)
{
StopAutoScroll();
return;
}
var matched = list.Where(item => item.status == 4 && item.pay_status == 2).ToList();
if (matched.Count == 0)
{
return;
}
// CEF 回调运行在后台线程,ObservableCollection 的更新需要切回 UI 线程
var dispatcher = Application.Current?.Dispatcher;
if (dispatcher == null || dispatcher.CheckAccess())
{
AddOrders(matched);
}
else
{
dispatcher.BeginInvoke(new Action(() =>
{
AddOrders(matched);
BottomMsg = $"正在读取订单,读取日期{matched.Last().OrderTime.ToString("yyyy-MM-dd")}";
}));
}
}
/// <summary> 将新订单写入总表,并按当前搜索关键字决定是否加入可见列表(自动去重) </summary>
private void AddOrders(IEnumerable<PddBrowserOrderInvoiceViewModel> items)
{
var keyword = SearchText?.Trim();
foreach (var item in items)
{
if (!string.IsNullOrEmpty(item.order_sn) && !_orderSns.Add(item.order_sn))
{
continue;
}
if (item.IsInvoiced == false)
{
item.IsInvoiced = item.order_buttons?.Any(a => a.brief_prompt == "发票详情") ?? false;
}
Orders.Add(item);
if (Config.IsSaveData)
{
OrderInvoiceInfoList.Add(new OrderInvoiceInfo
{
GoodsName = item.GoodsName,
order_amount = item.order_amount,
order_time = item.order_time,
order_sn = item.order_sn,
order_link_url = item.order_link_url,
IsInvoiced = item.IsInvoiced,
ThumbUrl = item.ThumbUrl
});
}
}
StatisticsInvoiceAmount();
SaveOrderInvoiceInfo();
}
#endregion
/// <summary>
/// 原型演示数据(后续可替换为真实接口数据)
/// </summary>
private Task InitSampleData()
{
string directoryPath = @"E:\820";
// 3. 获取所有 txt 文件
var txtFiles = Directory.GetFiles(directoryPath, "*.txt");
if (txtFiles.Length == 0)
{
Console.WriteLine("未找到任何 .txt 文件");
return Task.CompletedTask;
}
foreach (var filePath in txtFiles)
{
try
{
string jsonContent = File.ReadAllText(filePath);
parserData(jsonContent);
}
catch (Exception ex)
{
}
}
Orders.FirstOrDefault().IsInvoiced = true;
return Task.CompletedTask;
}
private const string inviceDataPath = "inviceData_{0}";
/// <summary>
/// 保存数据
/// </summary>
private void SaveOrderInvoiceInfo()
{
if (SelectedBuyer == null) return;
string path = string.Format(inviceDataPath, SelectedBuyer.buyer_id);
NaiBa.Util.Helper.ConfigHelper.SaveConfig<List<OrderInvoiceInfo>>(path, OrderInvoiceInfoList);
}
private void GetOrderInvoiceInfo()
{
if (SelectedBuyer == null) return;
string path = string.Format(inviceDataPath, SelectedBuyer.buyer_id);
if (File.Exists(NaiBa.Util.Helper.ConfigHelper.GetConfigUserPath(path)))
{
if (BaseMsgWindow.ShowConfirm("检测到已有旧数据是否加载?").DialogResult == true)
{
var data = NaiBa.Util.Helper.ConfigHelper.LoadConfig<List<OrderInvoiceInfo>>(path);
AddOrders(data.Select(a => new PddBrowserOrderInvoiceViewModel(a)));
}
}
}
#region 进度条
private string _bottomMsg = "";
public string BottomMsg
{
get => _bottomMsg;
set => SetValue(ref _bottomMsg, value);
}
private string _bottomRMsg = "";
public string BottomRMsg
{
get => _bottomRMsg;
set => SetValue(ref _bottomRMsg, value);
}
private double _progressValue;
public double ProgressValue
{
get => _progressValue;
set => SetValue(ref _progressValue, value);
}
private double _progressMaximum = 100;
public double ProgressMaximum
{
get => _progressMaximum;
set => SetValue(ref _progressMaximum, value);
}
public void SetProgress(int value, int maxValue)
{
StopUnknownTotalProgressTimer();
ProgressValue = value;
ProgressMaximum = maxValue;
}
#region 未知总数百分比模拟进度
private System.Threading.Timer _unknownTotalProgressTimer;
private DateTime _unknownTotalProgressStartTime;
/// <summary>
/// 开始未知总数的百分比模拟进度:80% 之前走得快,越到最后走得越慢,
/// 理论上永远走不到 95% 以上,直到调用 <see cref="CompleteProgress"/> 才会变成 100%。
/// </summary>
public void StartProgress()
{
StopUnknownTotalProgressTimer();
ProgressMaximum = 100;
ProgressValue = 0;
_unknownTotalProgressStartTime = DateTime.Now;
_unknownTotalProgressTimer = new System.Threading.Timer(_ => UpdateUnknownTotalProgress(), null, 0, 200);
}
/// <summary>
/// 结束进度:停止模拟,进度条立即变为 100%。
/// </summary>
public void CompleteProgress()
{
StopUnknownTotalProgressTimer();
ProgressMaximum = 100;
ProgressValue = 100;
}
private void UpdateUnknownTotalProgress()
{
try
{
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
var elapsed = (DateTime.Now - _unknownTotalProgressStartTime).TotalSeconds;
double progress;
if (elapsed <= 10)
{
// 前 10 秒内快速推进到 80%
progress = Math.Max(0, Math.Min(80, elapsed * 8));
}
else
{
// 超过 80% 后指数衰减越来越慢,渐近线为 95%,理论上永远走不到 95% 以上
progress = 95.0 - 15.0 * Math.Exp(-(elapsed - 10.0) / 20.0);
}
if (progress > 95) progress = 95;
ProgressValue = progress;
}));
}
catch
{
}
}
private void StopUnknownTotalProgressTimer()
{
if (_unknownTotalProgressTimer == null) return;
try
{
_unknownTotalProgressTimer.Dispose();
}
catch
{
}
_unknownTotalProgressTimer = null;
}
#endregion
#endregion
}
public class InvoiceConfig
{
public string Title { get; set; }
public string TaxNo { get; set; }
public decimal MaxAmount { get; set; }
public bool IsSaveData { get; set; }
}