利润统计 功能开放

统计页面增加新的统计功能
This commit is contained in:
chfei committed 2026-08-07 18:52:33 +08:00
1 parent f12a7f14ea
commit d25374956e
10 files changed
+571 -323

No files matched your search

-21
View File
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishUrlHistory>publish\</PublishUrlHistory>
<InstallUrlHistory />
<SupportUrlHistory />
<UpdateUrlHistory />
<BootstrapperUrlHistory />
<ErrorReportUrlHistory />
<FallbackCulture>zh-CN</FallbackCulture>
<VerifyUploadedFiles>false</VerifyUploadedFiles>
<ProjectView>ProjectFiles</ProjectView>
<ActiveDebugProfile>Production</ActiveDebugProfile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>
+109
View File
@@ -0,0 +1,109 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace _29WxOrderWpf.Common
{
/// <summary>
/// 让页面级 ScrollViewer 在鼠标悬停在子控件(如 DataGrid、图表等)上时也能随鼠标滚轮滚动。
/// 用法:在 ScrollViewer 上设置 common:ScrollViewerBehavior.FixMouseWheel="True"。
/// </summary>
public static class ScrollViewerBehavior
{
public static readonly DependencyProperty FixMouseWheelProperty =
DependencyProperty.RegisterAttached(
"FixMouseWheel",
typeof(bool),
typeof(ScrollViewerBehavior),
new PropertyMetadata(false, OnFixMouseWheelChanged));
public static bool GetFixMouseWheel(DependencyObject obj)
=> (bool)obj.GetValue(FixMouseWheelProperty);
public static void SetFixMouseWheel(DependencyObject obj, bool value)
=> obj.SetValue(FixMouseWheelProperty, value);
private static void OnFixMouseWheelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ScrollViewer scrollViewer))
{
return;
}
if ((bool)e.NewValue)
{
scrollViewer.PreviewMouseWheel += OnPreviewMouseWheel;
}
else
{
scrollViewer.PreviewMouseWheel -= OnPreviewMouseWheel;
}
}
private static void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Handled || !(sender is ScrollViewer scrollViewer) || scrollViewer.ScrollableHeight <= 0)
{
return;
}
if (!(e.OriginalSource is DependencyObject original))
{
return;
}
// 从鼠标所在控件向上查找,确认它属于当前 ScrollViewer
var current = original;
var inside = false;
while (current != null)
{
if (ReferenceEquals(current, scrollViewer))
{
inside = true;
break;
}
// 若鼠标位于一个能自行纵向滚动的内部 ScrollViewer 上(例如下拉列表、
// 启用了滚动的表格),则交给内部处理,避免抢走它的滚动。
if (current is ScrollViewer inner &&
inner.ComputedVerticalScrollBarVisibility == Visibility.Visible &&
inner.ScrollableHeight > 0)
{
return;
}
current = VisualTreeHelper.GetParent(current);
}
if (!inside)
{
return;
}
// 按系统默认的滚轮行数滚动当前页面
var lines = (int)(e.Delta / Mouse.MouseWheelDeltaForOneLine * SystemParameters.WheelScrollLines);
if (lines == 0)
{
lines = e.Delta > 0 ? 1 : -1;
}
if (lines < 0)
{
for (var i = 0; i < -lines; i++)
{
scrollViewer.LineDown();
}
}
else
{
for (var i = 0; i < lines; i++)
{
scrollViewer.LineUp();
}
}
e.Handled = true;
}
}
}
@@ -98,7 +98,7 @@
</WrapPanel>
<Button Visibility="Collapsed" c:DebugVisibility.Enable="True" Grid.Column="11" Content="利润统计" Style="{StaticResource BasePageButtonStyle}" Width="60" Height="28" Click="btn_ShowShopRevenueStatisticsView" Padding="4">
<Button Grid.Column="11" Content="利润统计" Style="{StaticResource BasePageButtonStyle}" Width="60" Height="28" Click="btn_ShowShopRevenueStatisticsView" Padding="4">
</Button>
</Grid>
@@ -1001,7 +1001,7 @@ namespace _29WxOrderWpf.Ctrls.OrderCtrls
if (chkEndDate.IsChecked == true && order.UpdateDateTime > dateEnd.SelectedDate.Value)
return true;
}
if (tb.Text == "创建时间")
if (tb.Text == "成交时间")
{
if (order.CreateDateTime < dateStart.SelectedDate.Value)
return true;
@@ -1707,7 +1707,7 @@ namespace _29WxOrderWpf.Ctrls.OrderCtrls
Title = "统计",
Content = new ShopRevenueStatisticsView(),
}.ShowDialog();
}.Show();
}
/// <summary>
@@ -137,6 +137,20 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
/// </summary>
[JsonProperty("external_procurement")]
public int ExternalProcurement { get; set; }
/// <summary>
/// 买手退款金额
/// </summary>
[JsonProperty("buyer_refund_amount")]
public int BuyerRefundAmount { get; set; }
/// <summary>
/// 买手退款状态
/// </summary>
[JsonProperty("buyer_refund_status")]
public string BuyerRefundStatus { get; set; }
}
/// <summary>
@@ -155,8 +169,8 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
/// <summary>退款金额(统计期间退款)</summary>
public decimal RefundAmount { get; set; }
/// <summary>商品销量(有效销量)</summary>
public int ProductSalesVolume { get; set; }
/// <summary>退款订单数</summary>
public int RefundOrderCount { get; set; }
/// <summary>平台服务费总额(元)</summary>
public decimal PlatformFeeTotal { get; set; }
@@ -172,6 +186,15 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
/// <summary>平均利润率(利润 / 采购金额,百分数,如 12.34 表示 12.34%)</summary>
public decimal AverageProfitRate { get; set; }
/// <summary>买手号付款金额(采购金额,元)</summary>
public decimal BuyerPaymentAmount { get; set; }
/// <summary>买手号退款金额(采购退款金额,元)</summary>
public decimal BuyerRefundAmount { get; set; }
/// <summary>买手号退款成功金额(采购退款金额,元)</summary>
public decimal BuyerRefundSuccessedAmount { get; set; }
/// <summary>买手号剩余退款金额(尚未成功退款的金额,元)</summary>
public decimal BuyerRefundRemainingAmount => BuyerRefundAmount - BuyerRefundSuccessedAmount;
}
/// <summary>
@@ -220,7 +243,8 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
/// <summary>销售额(元)</summary>
public decimal SalesAmount { get; set; }
/// <summary>采购金额(元)</summary>
public decimal BuyerPaymentAmount { get; set; }
/// <summary>利润(元)</summary>
public decimal Profit { get; set; }
@@ -272,6 +296,8 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
/// <summary>销售额(元)</summary>
public decimal SalesAmount { get; set; }
/// <summary>买手号付款金额(采购金额,元)</summary>
public decimal BuyerPaymentAmount { get; set; }
/// <summary>利润(元)</summary>
public decimal Profit { get; set; }
@@ -2,6 +2,7 @@
x:Class="_29WxOrderWpf.Ctrls.Statistics.PeriodRevenueStatisticsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="clr-namespace:_29WxOrderWpf.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:stat="clr-namespace:_29WxOrderWpf.Ctrls.Statistics"
@@ -46,7 +47,10 @@
</UserControl.Resources>
<Grid Background="#F5F7FB">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ScrollViewer
common:ScrollViewerBehavior.FixMouseWheel="True"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<StackPanel Margin="12">
<!-- ============ 查询条件工具栏(共享 UserControl) ============ -->
@@ -56,9 +60,7 @@
<stat:RevenueSummaryCards />
<!-- ============ 周期统计表格 ============ -->
<Border
Margin="6,8,6,12"
Style="{StaticResource CardBorderStyle}">
<Border Margin="6,8,6,12" Style="{StaticResource CardBorderStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -119,6 +121,11 @@
Binding="{Binding SalesAmount, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="销售额" />
<DataGridTextColumn
Width="*"
Binding="{Binding BuyerPaymentAmount, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="采购金额" />
<DataGridTextColumn
Width="*"
Binding="{Binding Profit, StringFormat={}¥{0:N2}}"
@@ -202,7 +209,10 @@
</ScrollViewer>
<!-- ============ 加载遮罩 ============ -->
<Border Background="#66FFFFFF" Visibility="{Binding IsLoading, Converter={StaticResource boolToVisibilityConverter}}" d:Visibility="Collapsed">
<Border
d:Visibility="Collapsed"
Background="#66FFFFFF"
Visibility="{Binding IsLoading, Converter={StaticResource boolToVisibilityConverter}}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ProgressBar
Width="240"
@@ -5,7 +5,8 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:_29WxOrderWpf.Ctrls.Statistics.ViewModels"
d:DataContext="{d:DesignInstance Type=vm:ShopRevenueStatisticsViewModel,IsDesignTimeCreatable=True}"
d:DataContext="{d:DesignInstance Type=vm:ShopRevenueStatisticsViewModel,
IsDesignTimeCreatable=True}"
mc:Ignorable="d">
<UserControl.Resources>
<!-- 卡片样式(白底、圆角、阴影) -->
@@ -26,25 +27,14 @@
</Style>
</UserControl.Resources>
<!-- 汇总卡片(2 行 x 5 列) -->
<Grid Margin="3,8,3,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 汇总卡片(2 行 x 6 列) -->
<UniformGrid
Margin="3,8,3,0"
Columns="7"
Rows="2">
<!-- 销售额 -->
<Border
Grid.Row="0"
Grid.Column="0"
Style="{StaticResource CardBorderStyle}">
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
@@ -65,10 +55,7 @@
</Border>
<!-- 有效订单 -->
<Border
Grid.Row="0"
Grid.Column="1"
Style="{StaticResource CardBorderStyle}">
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
@@ -88,11 +75,29 @@
</StackPanel>
</Border>
<!-- 采购金额 -->
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="采购金额" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.BuyerPaymentAmount, StringFormat={}¥{0:N2}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="采购付款金额" />
</StackPanel>
</Border>
<!-- 利润 -->
<Border
Grid.Row="0"
Grid.Column="2"
Style="{StaticResource CardBorderStyle}">
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
@@ -111,108 +116,28 @@
Text="实际利润" />
</StackPanel>
</Border>
<!-- 退款金额 -->
<Border
Grid.Row="0"
Grid.Column="3"
Style="{StaticResource CardBorderStyle}">
<!-- 平均利润率 -->
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="退款金额" />
Text="平均利润率" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.RefundAmount, StringFormat={}¥{0:N2}}" />
Text="{Binding Summary.AverageProfitRate, StringFormat={}{0:0.00}%}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="统计期间退款" />
Text="利润 / 采购金额" />
</StackPanel>
</Border>
<!-- 商品销量 -->
<Border
Grid.Row="0"
Grid.Column="4"
Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="商品销量" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.ProductSalesVolume, StringFormat={}{0:N0}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="有效销量" />
</StackPanel>
</Border>
<!-- 平台服务费总额 -->
<Border
Grid.Row="1"
Grid.Column="0"
Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="平台服务费总额" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.PlatformFeeTotal, StringFormat={}¥{0:N2}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="预计技术服务费" />
</StackPanel>
</Border>
<!-- 达人佣金 -->
<Border
Grid.Row="1"
Grid.Column="1"
Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="达人佣金" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.Commission, StringFormat={}¥{0:N2}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="佣金支出" />
</StackPanel>
</Border>
<!-- 客单价 -->
<Border
Grid.Row="1"
Grid.Column="2"
Style="{StaticResource CardBorderStyle}">
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
@@ -232,11 +157,135 @@
</StackPanel>
</Border>
<!-- 平台服务费总额 -->
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="平台服务费总额" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.PlatformFeeTotal, StringFormat={}¥{0:N2}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="预计技术服务费" />
</StackPanel>
</Border>
<!-- 退款金额 -->
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="退款金额" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.RefundAmount, StringFormat={}¥{0:N2}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="统计期间退款" />
</StackPanel>
</Border>
<!-- 退款订单 -->
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="退款订单" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.RefundOrderCount, StringFormat={}{0:N0}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="退款订单" />
</StackPanel>
</Border>
<!-- 采购退款成功金额 -->
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="总采购金额" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.BuyerRefundAmount, StringFormat={}¥{0:N2}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="售后采购金额" />
</StackPanel>
</Border>
<!-- 采购退款成功金额 -->
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="已退回采购金额" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.BuyerRefundSuccessedAmount, StringFormat={}¥{0:N2}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="售后已退回金额" />
</StackPanel>
</Border>
<!-- 采购退款成功金额 -->
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="待退回采购金额" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.BuyerRefundRemainingAmount, StringFormat={}¥{0:N2}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="待退回采购金额" />
</StackPanel>
</Border>
<!-- 退款率 -->
<Border
Grid.Row="1"
Grid.Column="3"
Style="{StaticResource CardBorderStyle}">
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
@@ -256,28 +305,27 @@
</StackPanel>
</Border>
<!-- 平均利润率 -->
<Border
Grid.Row="1"
Grid.Column="4"
Style="{StaticResource CardBorderStyle}">
<!-- 达人佣金 -->
<Border Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
FontSize="14"
Foreground="#888888"
Text="平均利润率" />
Text="达人佣金" />
<TextBlock
Margin="0,12,0,0"
FontSize="30"
FontWeight="Bold"
Foreground="#333333"
Text="{Binding Summary.AverageProfitRate, StringFormat={}{0:0.00}%}" />
Text="{Binding Summary.Commission, StringFormat={}¥{0:N2}}" />
<TextBlock
Margin="0,10,0,0"
FontSize="12"
Foreground="#999999"
Text="利润 / 采购金额" />
Text="佣金支出" />
</StackPanel>
</Border>
</Grid>
</UniformGrid>
</UserControl>
@@ -92,30 +92,39 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
public static bool IsRefunded(ShopRevenueStatisticsRow row)
{
if (row == null) return false;
return row.RefundStatus == 4 || row.RefundStatus == 11 || row.Status == StatusCancelAfterAfterSale;
return row.RefundStatus > 1 || row.RefundStatusString == "MERCHANT_REFUND_SUCCESS" || row.RefundStatusString == "MERCHANT_RETURN_SUCCESS";
}
/// <summary>是否有效订单(已付款且未退款)</summary>
public static bool IsValidOrder(ShopRevenueStatisticsRow row)
{
return IsPaidOrder(row) && !IsRefunded(row);
return !IsRefunded(row);
}
/// <summary>买手号退款成功</summary>
public static bool IsBuyerRefundSuccessful(ShopRevenueStatisticsRow row)
{
return row.BuyerRefundStatus == "退款成功";
}
/// <summary>汇总卡片</summary>
public static ShopRevenueSummary GetSummary(IEnumerable<ShopRevenueStatisticsRow> rows)
{
var list = (rows ?? new List<ShopRevenueStatisticsRow>()).ToList();
var list = (rows ?? new List<ShopRevenueStatisticsRow>()).Where(a => a.OutAmount > 0).ToList(); //买手付钱后的订单数量
var valid = list.Where(IsValidOrder).ToList();
var refunded = list.Where(IsRefunded).ToList();
decimal salesAmount = valid.Sum(r => (decimal)(r.OrderPrice)) / 100m;
decimal profit = valid.Sum(r => (decimal)(r.Profit )) / 100m;
decimal salesAmount = list.Sum(r => (decimal)(r.OrderPrice)) / 100m;
decimal profit = valid.Concat(refunded).Sum(r => (decimal)(r.Profit)) / 100m;
decimal refundAmount = refunded.Sum(r => (decimal)(r.OrderPrice)) / 100m;
decimal platformFeeTotal = valid.Sum(r => (decimal)(r.PredictCommissionFee)) / 100m;
decimal commission = valid.Sum(r => (decimal)(r.CommissionAmount)) / 100m;
decimal purchaseAmount = valid.Sum(r => (decimal)(r.PurchasePrice)) / 100m;
decimal purchaseAmount = valid.Sum(r => (decimal)(r.OutAmount)) / 100m;
decimal buyerPaymentAmount = list.Sum(r => (decimal)(r.OutAmount)) / 100m; //买手号付款金额(采购金额)
decimal buyerRefundAmount = refunded.Sum(r => (decimal)(r.OutAmount)) / 100m; //买手号退款金额(采购退款金额)
decimal buyerRefundSuccessedAmount = refunded.Where(IsBuyerRefundSuccessful).Sum(r => (decimal)(r.BuyerRefundAmount)) / 100m; //买手号退款成功金额
int validOrderCount = valid.Select(r => r.OrderId).Distinct().Count();
// 总订单数(退款率分母)= 已付款订单 + 已退款订单(含售后取消 200),不含未付款的 10/250
// 总订单数(退款率分母)= 已付款订单 + 已退款订单,不含未付款的 10/250
int totalOrderCount = list.Where(r => IsPaidOrder(r) || IsRefunded(r)).Select(r => r.OrderId).Distinct().Count();
int refundOrderCount = refunded.Select(r => r.OrderId).Distinct().Count();
@@ -125,12 +134,15 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
ValidOrderCount = validOrderCount,
Profit = profit,
RefundAmount = refundAmount,
ProductSalesVolume = valid.Count,
RefundOrderCount = refundOrderCount,
PlatformFeeTotal = platformFeeTotal,
Commission = commission,
AvgOrderAmount = validOrderCount == 0 ? 0m : salesAmount / validOrderCount,
RefundRate = totalOrderCount == 0 ? 0m : (decimal)refundOrderCount * 100m / totalOrderCount,
AverageProfitRate = purchaseAmount == 0m ? 0m : profit / purchaseAmount * 100m
AverageProfitRate = purchaseAmount == 0m ? 0m : profit / purchaseAmount * 100m,
BuyerPaymentAmount = buyerPaymentAmount,
BuyerRefundAmount = buyerRefundAmount,
BuyerRefundSuccessedAmount = buyerRefundSuccessedAmount,
};
}
@@ -164,14 +176,14 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
public static List<ShopSalesProportionItem> GetShopSalesProportion(IEnumerable<ShopRevenueStatisticsRow> rows)
{
var list = (rows ?? new List<ShopRevenueStatisticsRow>()).Where(IsValidOrder).ToList();
decimal total = list.Sum(r => (decimal)(r.OrderPrice )) / 100m;
decimal total = list.Sum(r => (decimal)(r.OrderPrice)) / 100m;
return list
.GroupBy(r => r.ShopName)
.Select(g => new ShopSalesProportionItem
{
ShopName = string.IsNullOrEmpty(g.Key) ? "未知店铺" : g.Key,
SalesAmount = g.Sum(r => (decimal)(r.OrderPrice )) / 100m,
SalesAmount = g.Sum(r => (decimal)(r.OrderPrice)) / 100m,
Percentage = total == 0m ? 0d : (double)(g.Sum(r => (decimal)(r.OrderPrice)) / 100m / total * 100m)
})
.OrderByDescending(i => i.SalesAmount)
@@ -181,7 +193,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
/// <summary>店铺统计表格</summary>
public static List<ShopStatisticsItem> GetShopStatistics(IEnumerable<ShopRevenueStatisticsRow> rows)
{
var list = (rows ?? new List<ShopRevenueStatisticsRow>()).ToList();
var list = (rows ?? new List<ShopRevenueStatisticsRow>()).Where(a => a.OutAmount > 0).ToList(); //买手付钱后的订单
return list
.GroupBy(r => r.ShopName)
@@ -189,15 +201,17 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
{
var valid = g.Where(IsValidOrder).ToList();
var refunded = g.Where(IsRefunded).ToList();
decimal salesAmount = valid.Sum(r => (decimal)(r.OrderPrice)) / 100m;
decimal salesAmount = g.Sum(r => (decimal)(r.OrderPrice)) / 100m; //与 GetSummary 一致:含已退款订单的销售额
int orderCount = valid.Select(r => r.OrderId).Distinct().Count();
decimal profit = valid.Sum(r => (decimal)(r.Profit)) / 100m;
decimal purchaseAmount = valid.Sum(r => (decimal)(r.PurchasePrice)) / 100m;
decimal profit = valid.Concat(refunded).Sum(r => (decimal)(r.Profit)) / 100m; //与 GetSummary 一致:有效+退款订单利润
decimal purchaseAmount = valid.Sum(r => (decimal)(r.OutAmount)) / 100m; //与 GetSummary 一致:买手付款金额(采购金额)
decimal buyerPaymentAmount = list.Sum(r => (decimal)(r.OutAmount)) / 100m; //买手号付款金额(采购金额)
return new ShopStatisticsItem
{
ShopName = string.IsNullOrEmpty(g.Key) ? "未知店铺" : g.Key,
OrderCount = orderCount,
SalesAmount = salesAmount,
BuyerPaymentAmount = buyerPaymentAmount,
Profit = profit,
RefundAmount = refunded.Sum(r => (decimal)(r.OrderPrice)) / 100m,
PlatformFeeTotal = valid.Sum(r => (decimal)(r.PredictCommissionFee)) / 100m,
@@ -213,7 +227,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
/// <summary>天/周/月周期统计表格(每天/每周/每月各一行,无数据的周期也补一行,金额单位:元)</summary>
public static List<ShopPeriodStatisticsItem> GetPeriodStatistics(IEnumerable<ShopRevenueStatisticsRow> rows, DateTime startDate, DateTime endDate, PeriodStatisticType periodType)
{
var list = (rows ?? new List<ShopRevenueStatisticsRow>()).ToList();
var list = (rows ?? new List<ShopRevenueStatisticsRow>()).Where(a => a.OutAmount > 0).ToList(); //买手付钱后的订单
// 有数据的周期
var map = list
@@ -224,16 +238,19 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
{
var valid = g.Where(IsValidOrder).ToList();
var refunded = g.Where(IsRefunded).ToList();
decimal salesAmount = valid.Sum(r => (decimal)(r.OrderPrice)) / 100m;
decimal profit = valid.Sum(r => (decimal)(r.Profit)) / 100m;
decimal purchaseAmount = valid.Sum(r => (decimal)(r.PurchasePrice)) / 100m;
decimal salesAmount = g.Sum(r => (decimal)(r.OrderPrice)) / 100m; //与 GetSummary 一致:含已退款订单的销售额
decimal profit = valid.Concat(refunded).Sum(r => (decimal)(r.Profit)) / 100m; //与 GetSummary 一致:有效+退款订单利润
decimal purchaseAmount = valid.Sum(r => (decimal)(r.OutAmount)) / 100m; //与 GetSummary 一致:买手付款金额(采购金额)
int orderCount = valid.Select(r => r.OrderId).Distinct().Count();
decimal buyerPaymentAmount = list.Sum(r => (decimal)(r.OutAmount)) / 100m; //买手号付款金额(采购金额)
return new ShopPeriodStatisticsItem
{
PeriodStart = g.Key,
Period = GetPeriodText(g.Key, periodType),
OrderCount = orderCount,
SalesAmount = salesAmount,
BuyerPaymentAmount = buyerPaymentAmount,
Profit = profit,
RefundAmount = refunded.Sum(r => (decimal)(r.OrderPrice)) / 100m,
PlatformFeeTotal = valid.Sum(r => (decimal)(r.PredictCommissionFee)) / 100m,
@@ -5,11 +5,12 @@
xmlns:charts="clr-namespace:_29WxOrderWpf.Ctrls.Statistics.Charts"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:_29WxOrderWpf.Ctrls.Statistics.ViewModels"
xmlns:stat="clr-namespace:_29WxOrderWpf.Ctrls.Statistics"
xmlns:vm="clr-namespace:_29WxOrderWpf.Ctrls.Statistics.ViewModels"
xmlns:wpf="clr-namespace:_29WxOrderWpf.Common"
d:Background="#F5F7FB"
d:DataContext="{d:DesignInstance Type=vm:ShopRevenueStatisticsViewModel,IsDesignTimeCreatable=True}"
d:DataContext="{d:DesignInstance Type=vm:ShopRevenueStatisticsViewModel,
IsDesignTimeCreatable=True}"
d:DesignHeight="900"
d:DesignWidth="1600"
wpf:DialogWindowProperty.ResizeMode="CanResizeWithGrip"
@@ -67,16 +68,14 @@
Background="Transparent">
<TabPanel
x:Name="HeaderPanel"
IsItemsHost="True"
Background="Transparent" />
Background="Transparent"
IsItemsHost="True" />
</Border>
<Border
Grid.Row="1"
Background="Transparent"
CornerRadius="0,8,8,8">
<ContentPresenter
x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent" />
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
@@ -124,158 +123,208 @@
<Grid Background="#F5F7FB">
<TabControl Style="{StaticResource StatisticsTabControlStyle}">
<TabItem
Header="综合统计"
Style="{StaticResource StatisticsTabItemStyle}">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<StackPanel Margin="12">
<TabItem Header="综合统计" Style="{StaticResource StatisticsTabItemStyle}">
<ScrollViewer
wpf:ScrollViewerBehavior.FixMouseWheel="True"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<StackPanel Margin="12">
<!-- ============ 查询条件工具栏(共享 UserControl) ============ -->
<stat:RevenueStatisticsSearchBar />
<!-- ============ 查询条件工具栏(共享 UserControl) ============ -->
<stat:RevenueStatisticsSearchBar />
<!-- ============ 汇总卡片(共享 UserControl) ============ -->
<stat:RevenueSummaryCards />
<!-- ============ 汇总卡片(共享 UserControl) ============ -->
<stat:RevenueSummaryCards />
<!-- ============ 图表区域 ============ -->
<Grid Margin="3,8,3,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<!-- ============ 图表区域 ============ -->
<Grid Margin="3,8,3,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<!-- 销售趋势 -->
<Border
Grid.Column="0"
Margin="6,6,3,6"
Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock Style="{StaticResource PanelTitleStyle}" Text="销售趋势" />
<charts:RevenueLineChart
Height="330"
Margin="0,10,0,0"
Points="{Binding TrendData}" />
</StackPanel>
</Border>
<!-- 销售趋势 -->
<Border
Grid.Column="0"
Margin="6,6,3,6"
Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock Style="{StaticResource PanelTitleStyle}" Text="销售趋势" />
<charts:RevenueLineChart
Height="330"
Margin="0,10,0,0"
Points="{Binding TrendData}" />
</StackPanel>
</Border>
<!-- 店铺销售占比 -->
<Border
Grid.Column="1"
Margin="3,6,6,6"
Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock Style="{StaticResource PanelTitleStyle}" Text="店铺销售占比 TOP 5" />
<charts:RevenuePieChart
Height="330"
Margin="0,10,0,0" MaxItems="5"
ItemsSource="{Binding ShopProportionData}" />
</StackPanel>
</Border>
</Grid>
<!-- 店铺销售占比 -->
<Border
Grid.Column="1"
Margin="3,6,6,6"
Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock Style="{StaticResource PanelTitleStyle}" Text="店铺销售占比 TOP 5" />
<charts:RevenuePieChart
Height="330"
Margin="0,10,0,0"
ItemsSource="{Binding ShopProportionData}"
MaxItems="5" />
</StackPanel>
</Border>
</Grid>
<!-- ============ 店铺统计表格 ============ -->
<Border Margin="6,8,6,12" Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
Margin="0,0,0,10"
Style="{StaticResource PanelTitleStyle}"
Text="店铺统计" />
<DataGrid
AlternatingRowBackground="#FAFAFA"
AutoGenerateColumns="False"
Background="Transparent"
BorderThickness="0"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeRows="False"
ColumnHeaderStyle="{StaticResource BaseDataGridColumnHeaderStyle}"
FontSize="13"
Foreground="#333333"
GridLinesVisibility="Horizontal"
HeadersVisibility="Column"
HorizontalGridLinesBrush="#EEEEEE"
IsReadOnly="True"
ItemsSource="{Binding ShopStatistics}"
RowHeight="46"
ScrollViewer.CanContentScroll="True"
SelectionMode="Single"
SelectionUnit="FullRow"
VerticalScrollBarVisibility="Disabled">
<DataGrid.Columns>
<DataGridTextColumn
Width="*"
Binding="{Binding ShopName}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="店铺" />
<DataGridTextColumn
Width="*"
Binding="{Binding OrderCount, StringFormat={}{0:N0}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="订单数" />
<DataGridTextColumn
Width="*"
Binding="{Binding SalesAmount, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="销售额" />
<DataGridTextColumn
Width="*"
Binding="{Binding Profit, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="利润" />
<DataGridTextColumn
Width="*"
Binding="{Binding RefundAmount, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="退款金额" />
<DataGridTextColumn
Width="*"
Binding="{Binding PlatformFeeTotal, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="平台服务费总额" />
<DataGridTextColumn
Width="*"
Binding="{Binding Commission, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="达人佣金" />
<DataGridTextColumn
Width="*"
Binding="{Binding AverageProfitRate, StringFormat={}{0:0.00}%}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="平均利润率" />
<DataGridTextColumn
Width="*"
Binding="{Binding AvgOrderAmount, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="客单价" />
</DataGrid.Columns>
</DataGrid>
<!-- ============ 店铺统计表格 ============ -->
<Border Margin="6,8,6,12" Style="{StaticResource CardBorderStyle}">
<StackPanel>
<TextBlock
Margin="0,0,0,10"
Style="{StaticResource PanelTitleStyle}"
Text="店铺统计" />
<DataGrid
AlternatingRowBackground="#FAFAFA"
AutoGenerateColumns="False"
Background="Transparent"
BorderThickness="0"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeRows="False"
ColumnHeaderStyle="{StaticResource BaseDataGridColumnHeaderStyle}"
FontSize="13"
Foreground="#333333"
GridLinesVisibility="Horizontal"
HeadersVisibility="Column"
HorizontalGridLinesBrush="#EEEEEE"
IsReadOnly="True"
ItemsSource="{Binding ShopStatistics}"
RowHeight="46"
ScrollViewer.CanContentScroll="True"
SelectionMode="Single"
SelectionUnit="FullRow"
VerticalScrollBarVisibility="Disabled">
<DataGrid.Columns>
<DataGridTextColumn
Width="*"
Binding="{Binding ShopName}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="店铺" />
<DataGridTextColumn
Width="*"
Binding="{Binding OrderCount, StringFormat={}{0:N0}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="订单数" />
<DataGridTextColumn
Width="*"
Binding="{Binding SalesAmount, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="销售额" />
<DataGridTextColumn
Width="*"
Binding="{Binding BuyerPaymentAmount, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="采购金额" />
<DataGridTextColumn
Width="*"
Binding="{Binding Profit, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="利润" />
<DataGridTextColumn
Width="*"
Binding="{Binding RefundAmount, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="退款金额" />
<DataGridTextColumn
Width="*"
Binding="{Binding PlatformFeeTotal, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="平台服务费总额" />
<DataGridTextColumn
Width="*"
Binding="{Binding Commission, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="达人佣金" />
<DataGridTextColumn
Width="*"
Binding="{Binding AverageProfitRate, StringFormat={}{0:0.00}%}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="平均利润率" />
<DataGridTextColumn
Width="*"
Binding="{Binding AvgOrderAmount, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="客单价" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Border>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</TabItem>
</ScrollViewer>
</TabItem>
<TabItem
Header="天统计"
Style="{StaticResource StatisticsTabItemStyle}">
<stat:PeriodRevenueStatisticsView PeriodType="Day" />
</TabItem>
<TabItem Header="天统计" Style="{StaticResource StatisticsTabItemStyle}">
<stat:PeriodRevenueStatisticsView PeriodType="Day" />
</TabItem>
<TabItem
Header="周统计"
Style="{StaticResource StatisticsTabItemStyle}">
<stat:PeriodRevenueStatisticsView PeriodType="Week" />
</TabItem>
<TabItem Header="周统计" Style="{StaticResource StatisticsTabItemStyle}">
<stat:PeriodRevenueStatisticsView PeriodType="Week" />
</TabItem>
<TabItem
Header="月统计"
Style="{StaticResource StatisticsTabItemStyle}">
<stat:PeriodRevenueStatisticsView PeriodType="Month" />
</TabItem>
<TabItem Header="月统计" Style="{StaticResource StatisticsTabItemStyle}">
<stat:PeriodRevenueStatisticsView PeriodType="Month" />
</TabItem>
</TabControl>
<!-- helper -->
<Border
Margin="0,20,30,0"
Padding="10"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{StaticResource pageColorBrush}"
BorderThickness="1"
Cursor="Hand"
MouseLeftButtonDown="help_mouseLeftButtonDown">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Black"
Foreground="White"
Text="利润计算逻辑" />
</Border>
<!-- Popup 弹出层 -->
<Popup
x:Name="InfoPopup"
AllowsTransparency="True"
Placement="Center"
PopupAnimation="Fade">
<Border
Padding="20"
Background="{StaticResource pageColorBrush}"
BorderThickness="1"
CornerRadius="4">
<Grid>
<Button
Margin="0,-10,-10,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="Transparent" Click="close"
Content="x" />
<TextBlock Margin="0,10,0,0"
x:Name="PopupText"
MaxWidth="500"
FontSize="16"
Foreground="White"
LineHeight="25"
Text="利润计算:真实店铺利润。已下单的订单金额减去采购金额减去平台手续费减去达人佣金=实际计算利润,未下单的订单不计入。 &#x0a;&#x0a;售后计算:只计算采购后发生的售后。发生售后金额减去发生售后采购总金额减去采购已退回金额=没退回金额(次月拉上月即可查到售后货损)发生售后店铺毛利自动减少。计算出店铺精准毛利。 &#x0a;&#x0a;纯利计算:你们减去中转仓金额和店铺运费险。人工等额外金额。即可精准算出。"
TextWrapping="WrapWithOverflow" />
</Grid>
</Border>
</Popup>
<!-- ============ 加载遮罩 ============ -->
<Border Background="#66FFFFFF" Visibility="{Binding IsLoading, Converter={StaticResource boolToVisibilityConverter}}" d:Visibility="Collapsed" >
<Border
d:Visibility="Collapsed"
Background="#66FFFFFF"
Visibility="{Binding IsLoading, Converter={StaticResource boolToVisibilityConverter}}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ProgressBar
Width="240"
@@ -21,5 +21,15 @@ namespace _29WxOrderWpf.Ctrls.Statistics
InitializeComponent();
DataContext = viewModel;
}
private void help_mouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
InfoPopup.IsOpen = true;
}
private void close(object sender, System.Windows.RoutedEventArgs e)
{
InfoPopup.IsOpen = false;
}
}
}