diff --git a/29WxOrderWpf/Converters/ImageUrlToBitmapImageConverter.cs b/29WxOrderWpf/Converters/ImageUrlToBitmapImageConverter.cs
index dfbf7e4..2063aaf 100644
--- a/29WxOrderWpf/Converters/ImageUrlToBitmapImageConverter.cs
+++ b/29WxOrderWpf/Converters/ImageUrlToBitmapImageConverter.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -14,27 +15,38 @@ namespace _29WxOrderWpf.Converters
{
public class ImageUrlToBitmapImageConverter : IValueConverter
{
+ /// 缓存上限,防止大量缩略图重复下载导致内存膨胀
+ private const int MaxCacheCount = 200;
+
+ private readonly ConcurrentDictionary _imageCache = new ConcurrentDictionary();
+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string url && Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
+ if (_imageCache.TryGetValue(url, out BitmapImage cached))
+ {
+ return cached;
+ }
+
try
{
// 直接加载绑定如果遇到有问题的网络图片会直接崩溃。
- //var bitMap = new BitmapImage(new Uri(url));
- BitmapImage bitMap = null;
- //bitMap = LoadImageFromUrl(url + "?imageView2/1/w/100/format/webp|imageMogr2/auto-orient=");
- bitMap = AllBase.ImageBuild.LoadBitmipImageFromUrl(url);
+ BitmapImage bitMap = AllBase.ImageBuild.LoadBitmipImageFromUrl(url);
+ if (bitMap != null)
+ {
+ if (_imageCache.Count >= MaxCacheCount)
+ {
+ _imageCache.Clear();
+ }
+ _imageCache[url] = bitMap;
+ }
return bitMap;
- //string errStr = "";
- //var bitmapImg = GetUrlImg(url, out errStr);
- //return bitmapImg;
}
catch
{
}
- //return Task.Run(() => new BitmapImage(new Uri(url))); // 使用 Task.Run 来异步加载图片。
}
return null;
}
diff --git a/29WxOrderWpf/Ctrls/OrderCtrls/OrderListCtrl.Command.cs b/29WxOrderWpf/Ctrls/OrderCtrls/OrderListCtrl.Command.cs
new file mode 100644
index 0000000..6625d74
--- /dev/null
+++ b/29WxOrderWpf/Ctrls/OrderCtrls/OrderListCtrl.Command.cs
@@ -0,0 +1,318 @@
+using _29WxOrderWpf.BaseWindows;
+using _29WxOrderWpf.Commands;
+using _29WxOrderWpf.Common;
+using _29WxOrderWpf.Ctrls.PDDInvoice;
+using _29WxOrderWpf.Ctrls.Statistics;
+using _29WxOrderWpf.Services;
+using AllBase.Helper;
+using AllBase.Model.DBModel;
+using NaiBa.Util.Extension;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using Work._29Tools.BLL;
+using static _29WxOrderWpf.Services.ShipGiftService;
+
+namespace _29WxOrderWpf.Ctrls.OrderCtrls;
+
+public partial class OrderListCtrl
+{
+ #region 统计
+ private DialogWindow _statisticsDialog;
+ public ICommand ShowShopRevenueStatisticsDialogViewCommand => new DelegateCommand(ShowShopRevenueStatisticsDialogView);
+
+ private void ShowShopRevenueStatisticsDialogView()
+ {
+ if (_statisticsDialog == null)
+ {
+ _statisticsDialog = new DialogWindow()
+ {
+ Title = "统计",
+ Content = new ShopRevenueStatisticsView(),
+
+ };
+ _statisticsDialog.Closed += (s, e) => _statisticsDialog = null;
+ }
+ _statisticsDialog.Show();
+ }
+ #endregion
+
+ #region 礼物
+ public ICommand MultipleShipGiftCommand => new AsyncDelegateCommand(MultipleShipGift);
+ ///
+ /// 批量发送礼品单
+ ///
+ ///
+ ///
+
+ private async Task MultipleShipGift()
+ {
+
+ try
+ {
+ var list = GlobalConfig.lstFilterOrder.Where(a => a.IsSelect).ToList();
+ int count = list.Count;
+ if (count == 0)
+ {
+ BaseMsgWindow.ShowError("请选取要发货的订单", "Error");
+ return;
+ }
+
+
+ GiftType giftType = GiftType._160;
+ if (cb_gift.SelectedIndex == 0)
+ {
+ giftType = GiftType._155;
+ }
+ else
+ {
+ giftType = GiftType._160;
+ }
+ int index = 1;
+ int sucessCount = 0;
+ int errorCount = 0;
+ foreach (oms_wei_order_info item in list)
+ {
+ try
+ {
+ var rst = await WxApiBLL.WxSearchAndDropshipCancelAsync(item.Shop, item);
+ if (rst == "success")
+ {
+ var ret = await _shipGiftService.Send(item.order_id, giftType);
+ if (ret)
+ {
+ sucessCount++;
+ item.gift_order_status = 1;
+ this.FilterOneOrder(item, true);
+ }
+ else
+ {
+ errorCount++;
+ }
+ }
+ else
+ {
+ errorCount++;
+ }
+ }
+ catch (Exception ex)
+ {
+ errorCount++;
+ }
+ finally
+ {
+ item.IsSelect = false;
+ }
+
+ SetProgress(index, count);
+ BottomMsg = $"礼品单发送进度:{index}/{count}\t\t{item.orderItems.FirstOrDefault()?.title ?? ""}";
+ index++;
+ }
+
+ BottomMsg = $"礼品单发送完成 成功:{sucessCount} 失败:{errorCount} 总数:{count}";
+ }
+ catch (Exception ex)
+ {
+ BaseMsgWindow.ShowError("礼品单发货失败\n" + ex.Message, "Error");
+ }
+ finally
+ {
+
+ }
+ }
+ #endregion
+ #region 同步订单
+ ///
+ /// 拉取微信订单到任务列表
+ ///
+ public ICommand PullWXOrderCommand => new AsyncDelegateCommand(PullWXOrder);
+
+ private async Task PullWXOrder()
+ {
+ try
+ {
+ var isAfterSales = GlobalConfig.OrderTabHeaderStr.StartsWith("需处理售后单");//是否售后
+
+ var selectedShops = GlobalConfig.ShopLoginList.Where(a => a.IsSelect).ToList();
+ if (selectedShops.Count == 0)
+ {
+ selectedShops = GlobalConfig.ShopLoginList.ToList();
+ }
+
+ var selectedOrders = GlobalConfig.lstFilterOrder.Where(a => a.IsSelect).ToList();
+ int count = selectedOrders.Count;
+ if (count > 0 && isAfterSales == false)//非售后订单
+ {
+ if (BaseMsgWindow.ShowConfirm($"已选取:{count}个订单", "同步订单", MainWindow.Instance, MsgWindowLevel.MEDIUM).DialogResult == false) return;
+ BottomMsg = $"订单同步进度....";
+ int index = 1;
+ int sucessCount = 0;
+ int errorCount = 0;
+ foreach (var item in selectedOrders)
+ {
+ var ret = await PullWXOrderService.Instance.PullOrderDetailAsync(item.shop_id, item.order_id); ;
+ if (ret)
+ {
+ sucessCount++;
+ this.GetOrder(item.order_id);
+ SetProgress(index, count);
+ item.IsSelect = false;
+ }
+ else
+ {
+ errorCount++;
+ }
+ BottomMsg = $"订单同步进度:{index}/{count}\t\t{item.orderItems.FirstOrDefault()?.title ?? ""}";
+ index++;
+ }
+ BottomMsg = $"订单同步进度完成 成功:{sucessCount} 失败:{errorCount} 总数:{count}";
+ }
+ else if (count > 0 && isAfterSales == true)//售后订单
+ {
+ if (BaseMsgWindow.ShowConfirm($"已选取:{count}个售后订单", "同步订单", MainWindow.Instance, MsgWindowLevel.MEDIUM).DialogResult == false) return;
+ //var dictionaryResult = selectedOrders
+ // .Where(o => o?.refundItems != null)
+ // .SelectMany(o => o.refundItems
+ // .Where(item => item != null)
+ // .Select(item => new { o.shop_id, item.after_sale_order_id }))
+ // .GroupBy(x => x.shop_id)
+ // .ToDictionary(
+ // g => g.Key,
+ // g => g.Select(x => x.after_sale_order_id).ToArray()
+ // );
+
+
+ //BottomMsg = $"售后订单同步进度....";
+ //foreach (var item in dictionaryResult)
+ //{
+ // var ret = await PullWXOrderService.Instance.PullAfterSaleOrdersAsync(item.Key, item.Value);
+ //}
+ //BottomMsg = $"售后同步任务已发送,等待同步完成";
+
+
+
+ int index = 1;
+ int sucessCount = 0;
+ int errorCount = 0;
+ BottomMsg = $"售后订单同步进度....";
+ foreach (KeyValuePair> item in selectedOrders.GroupBy(a => a.shop_id).ToDictionary(a => a.Key, a => a.ToList()))
+ {
+ long shopid = item.Key;
+ string shopName = item.Value.FirstOrDefault()?.shop_name ?? "";
+ string[] afterSaleOrderIds = item.Value.SelectMany(a => a.refundItems).Where(a => a != null).Select(a => a.after_sale_order_id).ToArray();
+ var ret = await PullWXOrderService.Instance.PullAfterSaleOrdersAsync(shopid, afterSaleOrderIds);
+ if (ret)
+ {
+ sucessCount++;
+ SetProgress(index, count);
+ item.Value.ForEach(a => a.IsSelect = false);
+ }
+ else
+ {
+ errorCount++;
+ }
+ BottomMsg = $"售后订单同步进度:{index}/{count}\t\t{shopName}";
+ index++;
+
+ }
+ BottomMsg = $"售后同步任务已发送,等待同步完成";
+ }
+ else
+ {
+ count = selectedShops.Count;
+ string prefix = $"已选取:{count}个商铺";
+ if (count == GlobalConfig.ShopLoginList.Count)
+ {
+ prefix = "已选取全部商铺";
+ }
+
+
+ var startTime = dateStart.SelectedDate.Value;
+ var endTime = dateEnd.SelectedDate.Value;
+
+ string msg = "";
+ if (isAfterSales)
+ {
+ msg = $"{prefix}";
+ }
+ else
+ {
+ msg = $"{prefix}\n\n时间范围:{startTime.ToString("yyyy-MM-dd")}-{endTime.ToString("yyyy-MM-dd")}";
+ }
+
+ if (BaseMsgWindow.ShowConfirm(msg, $"{(isAfterSales ? "售后" : "")}同步订单", MainWindow.Instance, MsgWindowLevel.MEDIUM).DialogResult == false) return;
+
+ if ((endTime - startTime).Days > 7)
+ {
+ throw new Exception("时间范围最大7天");
+ }
+
+ var ret = false;
+ if (isAfterSales)
+ {
+ ret = await PullWXOrderService.Instance.PullRefundOrderByShopsAsync(selectedShops.Select(a => a.ErpShopId.ParseToLong()).ToArray(), startTime, endTime);
+ }
+ else
+ {
+ ret = await PullWXOrderService.Instance.PullOrderByShopIdsAndTimeRangeAsync(selectedShops.Select(a => a.ErpShopId.ParseToLong()).ToArray(), startTime, endTime);
+ }
+ if (ret)
+ {
+ BaseMsgWindow.ShowInfo("同步任务已发送,等待同步完成\n任务详情查看任务列表");
+ }
+ else
+ {
+ BaseMsgWindow.ShowError("同步任务失败");
+ }
+ }
+
+
+ }
+ catch (Exception ex)
+ {
+ BaseMsgWindow.ShowError("同步订单失败\n" + ex.Message, "Error");
+ }
+ //var service = PullWXOrderService.Instance;
+ //foreach (var item in list)
+ //{
+ // await service.PullOrderDetailAsync(item.shop_id, item.order_id);
+ //}
+ }
+ #endregion
+ #region 显示任务列表
+ private DialogWindow _taskDialog;
+ public ICommand ShowTaskDialogViewCommand => new DelegateCommand(ShowTaskDialogView);
+
+ private void ShowTaskDialogView()
+ {
+ if (_taskDialog == null)
+ {
+ _taskDialog = new DialogWindow()
+ {
+ Title = "任务列表",
+ Content = new OrderTaskDialogView(),
+
+ };
+ _taskDialog.Closed += (s, e) => _taskDialog = null;
+ }
+ _taskDialog.Show();
+ }
+ #endregion
+
+ #region 拼多多发票
+ public ICommand ShowPDDInvoiceDialogCommand => new DelegateCommand(ShowPDDInvoiceDialog);
+
+ private void ShowPDDInvoiceDialog()
+ {
+ new DialogWindow
+ {
+ Title = "发票助手",
+ Content = new PDDInvoiceView()
+ }.ShowDialog();
+ }
+ #endregion
+}
diff --git a/29WxOrderWpf/Ctrls/OrderCtrls/OrderListCtrl.xaml b/29WxOrderWpf/Ctrls/OrderCtrls/OrderListCtrl.xaml
index c36abb5..129ea92 100644
--- a/29WxOrderWpf/Ctrls/OrderCtrls/OrderListCtrl.xaml
+++ b/29WxOrderWpf/Ctrls/OrderCtrls/OrderListCtrl.xaml
@@ -25,7 +25,8 @@
-
+
+
@@ -40,11 +41,8 @@
-
-
-
-
+
@@ -205,7 +203,8 @@
Padding="4"
Click="btnFilter_Click"
Content="过滤"
- Style="{StaticResource BasePageButtonStyle}" />
+ Style="{StaticResource BasePageButtonStyle}"
+ ToolTip="过滤" />
+
+
-
+
+
_statisticsWindow = null;
- }
- _statisticsWindow.Show();
-
- }
+
///
///
@@ -2066,263 +2051,7 @@ namespace _29WxOrderWpf.Ctrls.OrderCtrls
// set => SetProperty(ref _IsShipGift, value);
//}
- public ICommand MultipleShipGiftCommand => new AsyncDelegateCommand(MultipleShipGift);
- ///
- /// 批量发送礼品单
- ///
- ///
- ///
- private async Task MultipleShipGift()
- {
-
- try
- {
- var list = GlobalConfig.lstFilterOrder.Where(a => a.IsSelect).ToList();
- int count = list.Count;
- if (count == 0)
- {
- BaseMsgWindow.ShowError("请选取要发货的订单", "Error");
- return;
- }
-
-
- GiftType giftType = GiftType._160;
- if (cb_gift.SelectedIndex == 0)
- {
- giftType = GiftType._155;
- }
- else
- {
- giftType = GiftType._160;
- }
- int index = 1;
- int sucessCount = 0;
- int errorCount = 0;
- foreach (oms_wei_order_info item in list)
- {
- try
- {
- var rst = await WxApiBLL.WxSearchAndDropshipCancelAsync(item.Shop, item);
- if (rst == "success")
- {
- var ret = await _shipGiftService.Send(item.order_id, giftType);
- if (ret)
- {
- sucessCount++;
- item.gift_order_status = 1;
- this.FilterOneOrder(item, true);
- }
- else
- {
- errorCount++;
- }
- }
- else
- {
- errorCount++;
- }
- }
- catch (Exception ex)
- {
- errorCount++;
- }
- finally
- {
- item.IsSelect = false;
- }
-
- SetProgress(index, count);
- BottomMsg = $"礼品单发送进度:{index}/{count}\t\t{item.orderItems.FirstOrDefault()?.title ?? ""}";
- index++;
- }
-
- BottomMsg = $"礼品单发送完成 成功:{sucessCount} 失败:{errorCount} 总数:{count}";
- }
- catch (Exception ex)
- {
- BaseMsgWindow.ShowError("礼品单发货失败\n" + ex.Message, "Error");
- }
- finally
- {
-
- }
- }
-
- ///
- /// 拉取微信订单到任务列表
- ///
- public ICommand PullWXOrderCommand => new AsyncDelegateCommand(PullWXOrder);
-
- private async Task PullWXOrder()
- {
- try
- {
- var isAfterSales = GlobalConfig.OrderTabHeaderStr.StartsWith("需处理售后单");//是否售后
-
- var selectedShops = GlobalConfig.ShopLoginList.Where(a => a.IsSelect).ToList();
- if (selectedShops.Count == 0)
- {
- selectedShops = GlobalConfig.ShopLoginList.ToList();
- }
-
- var selectedOrders = GlobalConfig.lstFilterOrder.Where(a => a.IsSelect).ToList();
- int count = selectedOrders.Count;
- if (count > 0 && isAfterSales == false)//非售后订单
- {
- if (BaseMsgWindow.ShowConfirm($"已选取:{count}个订单", "同步订单", MainWindow.Instance, MsgWindowLevel.MEDIUM).DialogResult == false) return;
- BottomMsg = $"订单同步进度....";
- int index = 1;
- int sucessCount = 0;
- int errorCount = 0;
- foreach (var item in selectedOrders)
- {
- var ret = await PullWXOrderService.Instance.PullOrderDetailAsync(item.shop_id, item.order_id); ;
- if (ret)
- {
- sucessCount++;
- this.GetOrder(item.order_id);
- SetProgress(index, count);
- item.IsSelect = false;
- }
- else
- {
- errorCount++;
- }
- BottomMsg = $"订单同步进度:{index}/{count}\t\t{item.orderItems.FirstOrDefault()?.title ?? ""}";
- index++;
- }
- BottomMsg = $"订单同步进度完成 成功:{sucessCount} 失败:{errorCount} 总数:{count}";
- }
- else if (count > 0 && isAfterSales == true)//售后订单
- {
- if (BaseMsgWindow.ShowConfirm($"已选取:{count}个售后订单", "同步订单", MainWindow.Instance, MsgWindowLevel.MEDIUM).DialogResult == false) return;
- //var dictionaryResult = selectedOrders
- // .Where(o => o?.refundItems != null)
- // .SelectMany(o => o.refundItems
- // .Where(item => item != null)
- // .Select(item => new { o.shop_id, item.after_sale_order_id }))
- // .GroupBy(x => x.shop_id)
- // .ToDictionary(
- // g => g.Key,
- // g => g.Select(x => x.after_sale_order_id).ToArray()
- // );
-
-
- //BottomMsg = $"售后订单同步进度....";
- //foreach (var item in dictionaryResult)
- //{
- // var ret = await PullWXOrderService.Instance.PullAfterSaleOrdersAsync(item.Key, item.Value);
- //}
- //BottomMsg = $"售后同步任务已发送,等待同步完成";
-
-
-
- int index = 1;
- int sucessCount = 0;
- int errorCount = 0;
- BottomMsg = $"售后订单同步进度....";
- foreach (KeyValuePair> item in selectedOrders.GroupBy(a => a.shop_id).ToDictionary(a => a.Key, a => a.ToList()))
- {
- long shopid = item.Key;
- string shopName = item.Value.FirstOrDefault()?.shop_name ?? "";
- string[] afterSaleOrderIds = item.Value.SelectMany(a => a.refundItems).Where(a => a != null).Select(a => a.after_sale_order_id).ToArray();
- var ret = await PullWXOrderService.Instance.PullAfterSaleOrdersAsync(shopid, afterSaleOrderIds);
- if (ret)
- {
- sucessCount++;
- SetProgress(index, count);
- item.Value.ForEach(a => a.IsSelect = false);
- }
- else
- {
- errorCount++;
- }
- BottomMsg = $"售后订单同步进度:{index}/{count}\t\t{shopName}";
- index++;
-
- }
- BottomMsg = $"售后同步任务已发送,等待同步完成";
- }
- else
- {
- count = selectedShops.Count;
- string prefix = $"已选取:{count}个商铺";
- if (count == GlobalConfig.ShopLoginList.Count)
- {
- prefix = "已选取全部商铺";
- }
-
-
- var startTime = dateStart.SelectedDate.Value;
- var endTime = dateEnd.SelectedDate.Value;
-
- string msg = "";
- if (isAfterSales)
- {
- msg = $"{prefix}";
- }
- else
- {
- msg = $"{prefix}\n\n时间范围:{startTime.ToString("yyyy-MM-dd")}-{endTime.ToString("yyyy-MM-dd")}";
- }
-
- if (BaseMsgWindow.ShowConfirm(msg, $"{(isAfterSales ? "售后" : "")}同步订单", MainWindow.Instance, MsgWindowLevel.MEDIUM).DialogResult == false) return;
-
- if ((endTime - startTime).Days > 7)
- {
- throw new Exception("时间范围最大7天");
- }
-
- var ret = false;
- if (isAfterSales)
- {
- ret = await PullWXOrderService.Instance.PullRefundOrderByShopsAsync(selectedShops.Select(a => a.ErpShopId.ParseToLong()).ToArray(), startTime, endTime);
- }
- else
- {
- ret = await PullWXOrderService.Instance.PullOrderByShopIdsAndTimeRangeAsync(selectedShops.Select(a => a.ErpShopId.ParseToLong()).ToArray(), startTime, endTime);
- }
- if (ret)
- {
- BaseMsgWindow.ShowInfo("同步任务已发送,等待同步完成\n任务详情查看任务列表");
- }
- else
- {
- BaseMsgWindow.ShowError("同步任务失败");
- }
- }
-
-
- }
- catch (Exception ex)
- {
- BaseMsgWindow.ShowError("同步订单失败\n" + ex.Message, "Error");
- }
- //var service = PullWXOrderService.Instance;
- //foreach (var item in list)
- //{
- // await service.PullOrderDetailAsync(item.shop_id, item.order_id);
- //}
- }
-
- private DialogWindow _taskDialog;
- public ICommand ShowTaskDialogViewCommand => new DelegateCommand(ShowTaskDialogView);
-
- private void ShowTaskDialogView()
- {
- if (_taskDialog == null)
- {
- _taskDialog = new DialogWindow()
- {
- Title = "任务列表",
- Content = new OrderTaskDialogView(),
-
- };
- _taskDialog.Closed += (s, e) => _taskDialog = null;
- }
- _taskDialog.Show();
- }
}
public class OrderFilterConfig
diff --git a/29WxOrderWpf/Ctrls/PDDInvoice/PDDInvoiceView.xaml b/29WxOrderWpf/Ctrls/PDDInvoice/PDDInvoiceView.xaml
new file mode 100644
index 0000000..7efeba5
--- /dev/null
+++ b/29WxOrderWpf/Ctrls/PDDInvoice/PDDInvoiceView.xaml
@@ -0,0 +1,800 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/29WxOrderWpf/Ctrls/PDDInvoice/PDDInvoiceView.xaml.cs b/29WxOrderWpf/Ctrls/PDDInvoice/PDDInvoiceView.xaml.cs
new file mode 100644
index 0000000..0e640ac
--- /dev/null
+++ b/29WxOrderWpf/Ctrls/PDDInvoice/PDDInvoiceView.xaml.cs
@@ -0,0 +1,37 @@
+using _29WxOrderWpf.Ctrls.PDDInvoice.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace _29WxOrderWpf.Ctrls.PDDInvoice;
+
+///
+/// PDDInvoiceView.xaml 的交互逻辑
+///
+public partial class PDDInvoiceView : UserControl
+{
+ public PDDInvoiceView()
+ {
+ InitializeComponent();
+ DataContext = new PDDInvoiceViewModel();
+ }
+
+ private void txtSearch_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Enter && DataContext is PDDInvoiceViewModel viewModel)
+ {
+ viewModel.SearchCommand.Execute(null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceOrderModel.cs b/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceOrderModel.cs
new file mode 100644
index 0000000..128ff3a
--- /dev/null
+++ b/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceOrderModel.cs
@@ -0,0 +1,70 @@
+using AllBase.Model.Result;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using WPFCommon.Helpers;
+using WPFCommon.Models;
+
+namespace _29WxOrderWpf.Ctrls.PDDInvoice.ViewModels;
+
+///
+/// 订单发票Model
+///
+public class PddBrowserOrderInvoiceViewModel : BindableBaseEx
+{
+
+ public string buyerId { get; set; } // 买手Id
+ public string buyerName { get; set; } // 买手名称
+ ///
+ /// 订单价
+ ///
+ public int order_amount { get; set; }
+ ///
+ /// 实付款
+ ///
+ public string display_amount { get; set; }
+ ///
+ /// 下单时间
+ ///
+ public long order_time { get; set; }
+ ///
+ /// 订单号
+ ///
+ public string order_sn { get; set; }
+
+ public int status { get; set; }
+ public int pay_status { get; set; }
+ public string order_status_prompt { get; set; }
+ public List order_Goods { get; set; }
+
+
+ public List order_buttons { get; set; }
+
+
+ public string GoodsName { get => order_Goods?.FirstOrDefault()?.goods_name ?? ""; }
+ public bool IsInvoiced
+ {
+ get => order_buttons?.Any(a => a.button_code == 500) ?? false;
+ }
+
+ public int Quantity => order_Goods?.Sum(a => a.goods_number.Value) ?? 0;
+ /// 发票状态文案:已开票 / 待开票
+ public string InvoiceStatusText => IsInvoiced ? "已开票" : "待开票";
+ public DateTime OrderTime
+ {
+ get => DateTimeHelper.ConvertFromUnixTimestampInSeconds(order_time);
+ }
+ public string ThumbUrl=> order_Goods?.FirstOrDefault()?.thumb_url ?? "";
+ public string DisplayAmount => (order_amount / 100.0).ToString("F2");
+}
+
+public class OrderButtonItem
+{
+ public int button_code { get; set; }
+ public string brief_prompt { get; set; }
+ public ButtonTypeValue type_value { get; set; }
+}
+public class ButtonTypeValue
+{
+ public string Url { get; set; }
+}
\ No newline at end of file
diff --git a/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceViewModel.Browser.cs b/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceViewModel.Browser.cs
new file mode 100644
index 0000000..c661fa1
--- /dev/null
+++ b/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceViewModel.Browser.cs
@@ -0,0 +1,168 @@
+using _29WxOrderWpf.CefHelper;
+using _29WxOrderWpf.Commands;
+using AllBase;
+using CefSharp;
+using CefSharp.Handler;
+using CefSharp.Wpf;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Input;
+using Work._29Tools.CefTools;
+
+namespace _29WxOrderWpf.Ctrls.PDDInvoice.ViewModels;
+
+public partial class PDDInvoiceViewModel
+{
+ ///
+ /// 订单列表浏览器
+ ///
+ public ChromiumWebBrowser Browser { get; private set; }
+ ///
+ /// 内容浏览器
+ /// 订单详情
+ /// 发票详情
+ /// 申请发票
+ ///
+ public ChromiumWebBrowser BrowserDetail { get; private set; }
+
+ partial void InitBrowser()
+ {
+ BrowserZIndex = -1;
+ BrowserDetailZIndex = -1;
+
+ Browser = WPFCEFBrowser.CreateChromiumWebBrowser("about:blank", false);
+
+ LocalResourceRequestHandler resourceRequestHandler = new LocalResourceRequestHandler();
+ //resourceRequestHandler.BeforeResourceLoadEvent += RequestHandler_BeforeResourceLoad;
+ resourceRequestHandler.ResourceLoadCompleteEvent += RequestHandler_ResourceLoadComplete;
+ Browser.RequestHandler = new LocalRequestHandler(resourceRequestHandler);
+ Browser.LifeSpanHandler = new LocalLifeSpanHandler(PopWindowType.SelfWindow);
+ Browser.KeyboardHandler = new KeyboardHandler();
+
+
+ BrowserDetail = WPFCEFBrowser.CreateChromiumWebBrowser("about:blank", false);
+ LocalResourceRequestHandler detailResourceRequestHandler = new LocalResourceRequestHandler();
+ detailResourceRequestHandler.ResourceLoadCompleteEvent += DetailRequestHandler_ResourceLoadComplete;
+ BrowserDetail.RequestHandler = new LocalRequestHandler(resourceRequestHandler);
+ BrowserDetail.LifeSpanHandler = new LocalLifeSpanHandler(PopWindowType.SelfWindow);
+ BrowserDetail.KeyboardHandler = new KeyboardHandler();
+ }
+
+ public ICommand CloseOrderBrowserCommand => new DelegateCommand(HidenOrderBrowser);
+ public ICommand CloseInvoiceDetailCommand => new DelegateCommand(HidenOrderBrowserDetail);
+
+ public int BrowserZIndex
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+ public int BrowserDetailZIndex
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+ ///
+ /// 显示主浏览器
+ ///
+ public void ShowOrderBrowser()
+ {
+ BrowserZIndex = 10;
+ BrowserDetailZIndex = -1;
+ }
+ ///
+ /// 显示子浏览器
+ ///
+ public void ShowOrderBrowserDetail()
+ {
+ BrowserZIndex = -1;
+ BrowserDetailZIndex = 10;
+ }
+ public void HidenOrderBrowser()
+ {
+ BrowserZIndex = -1;
+ }
+ public void HidenOrderBrowserDetail()
+ {
+ BrowserDetailZIndex = -1;
+ }
+ public bool IsAutoScroll
+ {
+ get => GetValue();
+ set => SetValue(value);
+ }
+ private int top = 0;
+ private DateTime lastGetOrderDataTime = DateTime.Now;
+ private void RequestHandler_ResourceLoadComplete(object sender, string url, string postData, string responseData, IRequest request)
+ {
+ if (url.StartsWith("https://mobile.yangkeduo.com/orders.html"))
+ {
+ //if (isAutoScroll == false)
+ //{
+ // StartAutoScroll();
+ //}
+
+ StartAutoScroll();
+ }
+ if (url.StartsWith("https://mobile.yangkeduo.com/proxy/api/api/aristotle/order_list_v"))
+ {
+ lastGetOrderDataTime = DateTime.Now;
+ parserData(responseData);
+
+ //File.WriteAllText(@$"E:\{Guid.NewGuid().ToString()}.txt", responseData);
+ }
+ }
+ public void StartAutoScroll()
+ {
+ if (IsAutoScroll) return;
+ IsAutoScroll = true;
+ _cancellationTokenSource = new CancellationTokenSource();
+ _ = autoScroll(_cancellationTokenSource.Token);
+ }
+
+ // 停止滚动
+ public void StopAutoScroll()
+ {
+ IsAutoScroll = false;
+ _cancellationTokenSource?.Cancel();
+ _cancellationTokenSource?.Dispose();
+ _cancellationTokenSource = null;
+ }
+
+ private async Task autoScroll(CancellationToken token)
+ {
+ while (IsAutoScroll && !token.IsCancellationRequested)
+ {
+ if ((DateTime.Now - lastGetOrderDataTime).Seconds > 30) StopAutoScroll();
+
+ top += Randomx.GetId(450, 600);
+ var jsStr = @"window.scroll({
+ top: " + top + @",
+ left: 0,
+ behavior: 'smooth' // 可选,使滚动平滑
+ });";
+
+ var client = Browser.GetDevToolsClient();
+ if (client != null)
+ {
+ var result = client.Runtime.EvaluateAsync(jsStr, null, true, null, null, true, null, true);
+ result.Wait(5000);
+ }
+ await Task.Delay(Randomx.GetId(100, 300));
+ //Thread.Sleep();
+ }
+ }
+
+
+ private void DetailRequestHandler_ResourceLoadComplete(object sender, string url, string postData, string responseData, IRequest request)
+ {
+ if (url.StartsWith("https://mobile.yangkeduo.com/transac_return_compensation.html"))
+ {
+
+ }
+ }
+
+}
diff --git a/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceViewModel.cs b/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceViewModel.cs
new file mode 100644
index 0000000..182c4b8
--- /dev/null
+++ b/29WxOrderWpf/Ctrls/PDDInvoice/ViewModels/PDDInvoiceViewModel.cs
@@ -0,0 +1,391 @@
+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;
+
+///
+/// 拼多多发票助手 视图模型
+///
+public partial class PDDInvoiceViewModel : BindableBaseEx
+{
+ private readonly ObservableCollection _allOrders = new();
+ private readonly HashSet _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();
+
+ BuyerLoginList = GlobalConfig.PDDBuyerLoginList;
+ //Orders = new ObservableCollection(_allOrders);
+
+ //_ = InitSampleData();
+
+ InitBrowser();
+ }
+ partial void InitBrowser();
+
+
+
+ private void parserData(string data)
+ {
+ List list = JsonHelper.DeserializeJsonToList(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)));
+ }
+ }
+
+ /// 将新订单写入总表,并按当前搜索关键字决定是否加入可见列表(自动去重)
+ private void AddOrders(IEnumerable 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 BuyerLoginList { get; set; } = new ObservableCollectionThreadSafe();
+ 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();
+
+ }
+
+ /// 订单列表
+ public ObservableCollection Orders { get; }
+
+ /// 搜索关键字:订单号 / 商品名称 / 买家
+ public string SearchText
+ {
+ get => _searchText;
+ set => SetProperty(ref _searchText, value, nameof(SearchText));
+ }
+
+ /// 开票弹窗是否打开
+ public bool IsInvoiceDialogOpen
+ {
+ get => _isInvoiceDialogOpen;
+ set => SetProperty(ref _isInvoiceDialogOpen, value, nameof(IsInvoiceDialogOpen));
+ }
+
+ /// 发票详情弹窗是否打开
+ public bool IsInvoiceDetailOpen
+ {
+ get => _isInvoiceDetailOpen;
+ set => SetProperty(ref _isInvoiceDetailOpen, value, nameof(IsInvoiceDetailOpen));
+ }
+
+ /// 当前操作的订单
+ public PddBrowserOrderInvoiceViewModel SelectedOrder
+ {
+ get => _selectedOrder;
+ set => SetProperty(ref _selectedOrder, value, nameof(SelectedOrder));
+ }
+
+ /// 开票类型
+ public string InvoiceType
+ {
+ get => _invoiceType;
+ set => SetProperty(ref _invoiceType, value, nameof(InvoiceType));
+ }
+
+ /// 发票抬头
+ public string InvoiceTitle
+ {
+ get => _invoiceTitle;
+ set => SetProperty(ref _invoiceTitle, value, nameof(InvoiceTitle));
+ }
+
+ /// 税号
+ 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(OpenInvoice);
+
+ public ICommand ShowInvoiceCommand => new DelegateCommand(ShowInvoice);
+
+ public ICommand ViewOrderCommand => new DelegateCommand(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();
+ }
+
+
+ /// 按 订单号 / 商品名称 过滤订单
+ 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}");
+ }
+
+ ///
+ /// 原型演示数据(后续可替换为真实接口数据)
+ ///
+ 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;
+
+ ///
+ /// 开始未知总数的百分比模拟进度:80% 之前走得快,越到最后走得越慢,
+ /// 理论上永远走不到 95% 以上,直到调用 才会变成 100%。
+ ///
+ public void StartProgress()
+ {
+ StopUnknownTotalProgressTimer();
+ ProgressMaximum = 100;
+ ProgressValue = 0;
+ _unknownTotalProgressStartTime = DateTime.Now;
+ _unknownTotalProgressTimer = new System.Threading.Timer(_ => UpdateUnknownTotalProgress(), null, 0, 200);
+ }
+
+ ///
+ /// 结束进度:停止模拟,进度条立即变为 100%。
+ ///
+ 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
+}
+
diff --git a/29WxOrderWpf/Ctrls/Statistics/ShopRevenueStatisticsView.xaml b/29WxOrderWpf/Ctrls/Statistics/ShopRevenueStatisticsView.xaml
index 008a6df..a01248e 100644
--- a/29WxOrderWpf/Ctrls/Statistics/ShopRevenueStatisticsView.xaml
+++ b/29WxOrderWpf/Ctrls/Statistics/ShopRevenueStatisticsView.xaml
@@ -122,7 +122,7 @@
-
+
-
+
+
+
+
@@ -307,11 +314,13 @@
Margin="0,-10,-10,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
- Background="Transparent" Click="close"
+ Background="Transparent"
+ Click="close"
Content="x" />
-