392 lines
10 KiB
C#
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
|
|
}
|
|
|