Files
29WxOrder/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceViewModel.cs
T
2026-08-20 14:56:38 +08:00

392 lines
10 KiB
C#

using _29WxOrderWpf.CefHelper;
using _29WxOrderWpf.Commands;
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 NaiBa.Util.Extension;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using Work._29Tools.CefTools;
using WPFCommon.Models;
namespace _29WxOrderWpf.Ctrls.PDDInvoice.ViewModels;
/// <summary>
/// 拼多多发票助手 视图模型
/// </summary>
public partial class PDDInvoiceViewModel : BindableBaseEx
{
private readonly ObservableCollection<PddBrowserOrderInvoiceViewModel> _allOrders = new();
private readonly HashSet<string> _orderSns = new();
private string _searchText = "";
private PddBrowserOrderInvoiceViewModel _selectedOrder;
private bool _isInvoiceDialogOpen;
private bool _isInvoiceDetailOpen;
private string _invoiceType = "电子普通发票";
private string _invoiceTitle = "XX科技有限公司";
private string _taxNo = "91110000XXXXXXXXXX";
private CancellationTokenSource _cancellationTokenSource; public PDDInvoiceViewModel()
{
Orders = new ObservableCollection<PddBrowserOrderInvoiceViewModel>();
BuyerLoginList = GlobalConfig.PDDBuyerLoginList;
//Orders = new ObservableCollection<PDDInvoiceOrderModel>(_allOrders);
//_ = InitSampleData();
InitBrowser();
}
partial void InitBrowser();
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)));
}
}
/// <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;
}
_allOrders.Add(item);
if (IsKeywordMatch(item, keyword))
{
Orders.Add(item);
}
}
}
public ObservableCollectionThreadSafe<BuyerLoginInfo> BuyerLoginList { get; set; } = new ObservableCollectionThreadSafe<BuyerLoginInfo>();
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 ObservableCollection<PddBrowserOrderInvoiceViewModel> Orders { get; }
/// <summary> 搜索关键字:订单号 / 商品名称 / 买家 </summary>
public string SearchText
{
get => _searchText;
set => SetProperty(ref _searchText, value, nameof(SearchText));
}
/// <summary> 开票弹窗是否打开 </summary>
public bool IsInvoiceDialogOpen
{
get => _isInvoiceDialogOpen;
set => SetProperty(ref _isInvoiceDialogOpen, value, nameof(IsInvoiceDialogOpen));
}
/// <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 InvoiceType
{
get => _invoiceType;
set => SetProperty(ref _invoiceType, value, nameof(InvoiceType));
}
/// <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));
}
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 CloseInvoiceDialogCommand => new DelegateCommand(() => IsInvoiceDialogOpen = false);
public ICommand ConfirmInvoiceCommand => new DelegateCommand(ConfirmInvoice);
public ICommand ReadOrderCommand => new DelegateCommand(ReadOrder, () => !IsAutoScroll);
private void ReadOrder()
{
Browser.Load("https://mobile.yangkeduo.com/orders.html");
ShowOrderBrowser();
}
/// <summary> 按 订单号 / 商品名称 过滤订单 </summary>
private void Search()
{
var keyword = SearchText?.Trim();
Orders.Clear();
foreach (var order in _allOrders.Where(o => IsKeywordMatch(o, keyword)))
{
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 = "";
Search();
}
private void OpenInvoice(PddBrowserOrderInvoiceViewModel order)
{
SelectedOrder = order;
BrowserDetail.Load($"https://mobile.yangkeduo.com/transac_return_compensation.html?_t_timestamp=transac_modify_invoice&order_sn={order.order_sn}");
ShowOrderBrowserDetail();
//InvoiceType = string.IsNullOrEmpty(order.InvoiceType) ? "电子普通发票" : order.InvoiceType;
//InvoiceTitle = string.IsNullOrEmpty(order.InvoiceTitle) ? "XX科技有限公司" : order.InvoiceTitle;
//TaxNo = string.IsNullOrEmpty(order.TaxNo) ? "91110000XXXXXXXXXX" : order.TaxNo;
//IsInvoiceDialogOpen = true;
}
private void ConfirmInvoice()
{
if (SelectedOrder == null)
return;
//SelectedOrder.IsInvoiced = true;
//SelectedOrder.InvoiceType = InvoiceType;
//SelectedOrder.InvoiceTitle = InvoiceTitle;
//SelectedOrder.TaxNo = TaxNo;
//SelectedOrder.InvoiceNo = "25" + DateTime.Now.ToString("MMddHHmmss");
//SelectedOrder.InvoiceAmountText = SelectedOrder.AmountText;
//SelectedOrder.InvoiceTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
IsInvoiceDialogOpen = false;
//MessageBox.Show($"订单 {SelectedOrder.OrderNo} 开票成功");
}
private void ShowInvoice(PddBrowserOrderInvoiceViewModel order)
{
SelectedOrder = order;
IsInvoiceDetailOpen = true;
}
private void ViewOrder(PddBrowserOrderInvoiceViewModel order)
{
//MessageBox.Show($"查看订单:{order.OrderNo}");
}
/// <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)
{
}
}
return Task.CompletedTask;
}
#region 进度条
private string _bottomMsg = "";
public string BottomMsg
{
get => _bottomMsg;
set => SetValue(ref _bottomMsg, 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
}