添加项目文件。

This commit is contained in:
“hsc”
2026-07-29 13:12:27 +08:00
parent d6da766e2d
commit 8cf45d36b9
297 changed files with 35814 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
using ExportModule.Views;
using System.Reflection;
namespace ExportModule
{
public class ExportModule: IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
IRegionManager regionManager = containerProvider.Resolve<IRegionManager>();
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ExportView>("ExportView");
}
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ClosedXML" Version="0.104.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ORM\ORM.csproj" />
<ProjectReference Include="..\Service\Service.csproj" />
<ProjectReference Include="..\UIShare\UIShare.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,262 @@
using ClosedXML.Excel;
using Logger;
using Microsoft.Win32;
using Model;
using Model.Entity;
using Model.Models;
using Service.Interface;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using UIShare.ViewModelBase;
namespace ExportModule.ViewModels
{
public class ExportViewModel : NavigateViewModelBase, IRegionMemberLifetime, IDisposable
{
#region
public bool KeepAlive => true;
private ObservableCollection<TestReportModel> _testReportModelList = new();
public ObservableCollection<TestReportModel> TestReportModelList
{
get { return _testReportModelList; }
set { SetProperty(ref _testReportModelList, value); }
}
private DateTime? _startTime;
public DateTime? StartTime
{
get => _startTime;
set => SetProperty(ref _startTime, value);
}
private DateTime? _endTime;
public DateTime? EndTime
{
get => _endTime;
set => SetProperty(ref _endTime, value);
}
private string _selectedScope = "全部";
public string SelectedScope
{
get => _selectedScope;
set => SetProperty(ref _selectedScope, value);
}
public ObservableCollection<string> ScopeOptions { get; } = new()
{
"全部",
"TestCell1", "TestCell2", "TestCell3", "TestCell4",
"TestCell5", "TestCell6", "TestCell7", "TestCell8"
};
private string _statusMessage = "就绪";
public string StatusMessage
{
get => _statusMessage;
set => SetProperty(ref _statusMessage, value);
}
private int _totalCount;
public int TotalCount
{
get => _totalCount;
set => SetProperty(ref _totalCount, value);
}
private TestReportModel? _selectedTestReport;
public TestReportModel? SelectedTestReport
{
get => _selectedTestReport;
set => SetProperty(ref _selectedTestReport, value);
}
#endregion
#region
public ICommand LoadedCommand { get; }
public ICommand QueryCommand { get; }
public ICommand ExportCommand { get; }
#endregion
#region
private readonly ITestReportService _testReportService;
#endregion
public ExportViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_testReportService = containerProvider.Resolve<ITestReportService>();
LoadedCommand = new AsyncDelegateCommand(OnLoad);
QueryCommand = new AsyncDelegateCommand(OnQuery);
ExportCommand = new AsyncDelegateCommand(OnExport);
}
#region
/// <summary>
/// 加载全部(无筛选条件)
/// </summary>
private async Task OnLoad()
{
var result = await _testReportService.GetFilterList();
if (result.IsSuccess)
{
TestReportModelList = new ObservableCollection<TestReportModel>(result.Data ?? new List<TestReportModel>());
TotalCount = TestReportModelList.Count;
ShowInfoMessageBox($"加载完成,共 {TotalCount} 条记录", () => { });
}
else
{
TestReportModelList = new ObservableCollection<TestReportModel>();
TotalCount = 0;
ShowErrorMessageBox($"加载失败:{result.Msg}", () => { });
LoggerHelper.Error(result.Msg);
}
}
/// <summary>
/// 查询(根据开始日期、结束日期、台架名称筛选)
/// </summary>
private async Task OnQuery()
{
// 处理结束日期:若用户只选了日期没选时间,默认到当天末尾
DateTime? queryEnd = EndTime;
if (queryEnd.HasValue)
{
queryEnd = queryEnd.Value.Date.AddDays(1).AddSeconds(-1);
}
string? scope = SelectedScope == "全部" ? null : SelectedScope;
var result = await _testReportService.GetFilterList(StartTime, queryEnd, scope);
if (result.IsSuccess)
{
TestReportModelList = new ObservableCollection<TestReportModel>(result.Data ?? new List<TestReportModel>());
TotalCount = TestReportModelList.Count;
ShowInfoMessageBox($"查询完成,共 {TotalCount} 条记录", () => { });
}
else
{
TestReportModelList = new ObservableCollection<TestReportModel>();
TotalCount = 0;
ShowErrorMessageBox($"查询失败:{result.Msg}", () => { });
LoggerHelper.Error(result.Msg);
}
}
/// <summary>
/// 导出 Excel根据用户在表格中选中的 TestReportModel.TestRoundId 查询并导出完整步骤数据
/// </summary>
private async Task OnExport()
{
if (SelectedTestReport == null)
{
ShowErrorMessageBox("请先在列表中选择一条测试记录。", () => { });
return;
}
StatusMessage = "正在查询导出数据...";
var entityResult = await _testReportService.GetByTestRoundIdAsync(SelectedTestReport.TestRoundId);
if (!entityResult.IsSuccess || entityResult.Data == null || entityResult.Data.Count == 0)
{
ShowErrorMessageBox("未找到该测试记录的详细步骤数据。", () => { });
return;
}
var dialog = new SaveFileDialog
{
Filter = "Excel 工作簿 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*",
DefaultExt = ".xlsx",
FileName = $"测试报告_{SelectedTestReport.Scope}_{SelectedTestReport.StartTime:yyyyMMdd_HHmmss}.xlsx"
};
if (dialog.ShowDialog() != true) return;
var entities = entityResult.Data;
StatusMessage = $"正在导出 {entities.Count} 条步骤记录...";
await Task.Run(() => ExportToExcel(dialog.FileName, entities));
ShowInfoMessageBox($"导出完成,共 {entities.Count} 条步骤记录,已保存至:{dialog.FileName}", () => { });
}
#endregion
#region
private static void ExportToExcel(string filePath, List<TestReportEntity> entities)
{
using var workbook = new XLWorkbook();
var ws = workbook.Worksheets.Add("测试报告");
// 表头
string[] headers = {
"Id", "CreateTime", "TestRoundId", "Scope", "FileName",
"StepIndex", "StepName", "StepType", "MethodName", "MethodFullName",
"Result", "RunTimeMs", "OutputValue", "Depth", "IsErrorStep", "LoopRemaining"
};
for (int c = 0; c < headers.Length; c++)
{
ws.Cell(1, c + 1).Value = headers[c];
}
// 标题行样式
var headerRange = ws.Range(1, 1, 1, headers.Length);
headerRange.Style.Font.Bold = true;
headerRange.Style.Fill.BackgroundColor = XLColor.FromArgb(0xECEFF4);
headerRange.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
headerRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
// 填充数据
for (int i = 0; i < entities.Count; i++)
{
var e = entities[i];
int row = i + 2;
ws.Cell(row, 1).Value = e.Id;
ws.Cell(row, 2).Value = e.CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
ws.Cell(row, 3).Value = e.TestRoundId.ToString();
ws.Cell(row, 4).Value = e.Scope ?? "";
ws.Cell(row, 5).Value = e.FileName ?? "";
ws.Cell(row, 6).Value = e.StepIndex;
ws.Cell(row, 7).Value = e.StepName ?? "";
ws.Cell(row, 8).Value = e.StepType ?? "";
ws.Cell(row, 9).Value = e.MethodName ?? "";
ws.Cell(row, 10).Value = e.MethodFullName ?? "";
ws.Cell(row, 11).Value = e.Result;
ws.Cell(row, 12).Value = e.RunTimeMs;
ws.Cell(row, 13).Value = e.OutputValue ?? "";
ws.Cell(row, 14).Value = e.Depth;
ws.Cell(row, 15).Value = e.IsErrorStep ? "是" : "否";
ws.Cell(row, 16).Value = e.LoopRemaining;
// 数据行边框
var dataRange = ws.Range(row, 1, row, headers.Length);
dataRange.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
dataRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
}
ws.Columns().AdjustToContents();
workbook.SaveAs(filePath);
}
#endregion
#region
public override void OnNavigatedTo(NavigationContext navigationContext)
{
}
public void Dispose()
{
}
#endregion
}
}

View File

@@ -0,0 +1,204 @@
<UserControl x:Class="ExportModule.Views.ExportView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
d:DesignHeight="700"
d:DesignWidth="1200">
<UserControl.Resources>
<converters:LessThanConverter x:Key="LessThanConverter"/>
</UserControl.Resources>
<Border Background="#F5F7FA">
<Grid x:Name="RootGrid" Margin="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- ========== 标题 ========== -->
<TextBlock Grid.Row="0"
Text="测试报告导出"
FontSize="20" FontWeight="Bold"
Margin="4,4,0,12"/>
<!-- ========== 主内容卡片 ========== -->
<Border Grid.Row="1"
Background="White"
BorderBrush="#DDD" BorderThickness="1"
CornerRadius="4">
<Grid>
<Grid.RowDefinitions>
<!-- 工具栏 -->
<RowDefinition Height="Auto"/>
<!-- 数据表格 -->
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- ====== 工具栏(筛选条件 + 操作按钮) ====== -->
<Border Grid.Row="0"
Background="#ECEFF4"
Padding="12,8"
BorderBrush="#DDD" BorderThickness="0,0,0,1">
<!-- 第一行:筛选条件 -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 开始日期 -->
<TextBlock Grid.Column="0"
Text="开始日期:"
VerticalAlignment="Center"
FontWeight="SemiBold"
Margin="0,0,4,0"/>
<DatePicker Grid.Column="1"
materialDesign:HintAssist.Hint=""
SelectedDate="{Binding StartTime}"
VerticalAlignment="Center"
Margin="0,0,16,0"/>
<!-- 结束日期 -->
<TextBlock Grid.Column="2"
Text="结束日期:"
VerticalAlignment="Center"
FontWeight="SemiBold"
Margin="0,0,4,0"/>
<DatePicker Grid.Column="3"
materialDesign:HintAssist.Hint=""
SelectedDate="{Binding EndTime}"
VerticalAlignment="Center"
Margin="0,0,16,0"/>
<!-- 台架选择 -->
<TextBlock Grid.Column="4"
Text="台架:"
VerticalAlignment="Center"
FontWeight="SemiBold"
Margin="0,0,4,0"/>
<ComboBox Grid.Column="5"
materialDesign:HintAssist.Hint=""
ItemsSource="{Binding ScopeOptions}"
SelectedItem="{Binding SelectedScope}"
VerticalAlignment="Center"
Margin="0,0,16,0"/>
<!-- 操作按钮 -->
<Button Grid.Column="6"
Content="查询"
Command="{Binding QueryCommand}"
Style="{StaticResource MaterialDesignFlatButton}"
Padding="16,6"
Margin="4,0"
VerticalAlignment="Center"/>
<Button Grid.Column="7"
Content="导出 Excel"
Command="{Binding ExportCommand}"
Style="{StaticResource MaterialDesignFlatButton}"
Padding="16,6"
Margin="4,0"
VerticalAlignment="Center"/>
<Button Grid.Column="8"
Content="加载全部"
Command="{Binding LoadedCommand}"
Style="{StaticResource MaterialDesignFlatButton}"
Padding="16,6"
Margin="4,0"
VerticalAlignment="Center"/>
</Grid>
</Border>
<!-- ====== 数据展示区 ====== -->
<DataGrid Grid.Row="1"
ItemsSource="{Binding TestReportModelList}"
AutoGenerateColumns="False"
IsReadOnly="True"
RowHeaderWidth="0"
BorderThickness="0"
Background="White"
AlternatingRowBackground="#F5F7FA"
SelectionMode="Single"
SelectedItem="{Binding SelectedTestReport}"
Margin="8">
<DataGrid.Columns>
<DataGridTextColumn Header="TestRoundId"
Width="280"
Binding="{Binding TestRoundId}"/>
<DataGridTextColumn Header="台架名称"
Width="120"
Binding="{Binding Scope}"/>
<DataGridTextColumn Header="开始时间"
Width="180"
Binding="{Binding StartTime, StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"/>
<DataGridTextColumn Header="结束时间"
Width="180"
Binding="{Binding EndTime, StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"/>
<DataGridTextColumn Header="文件名"
Width="*"
Binding="{Binding FileName}"/>
</DataGrid.Columns>
<DataGrid.Resources>
<!-- 标题行样式:与项目风格一致 -->
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#ECEFF4"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="BorderBrush" Value="#DDD"/>
<Setter Property="BorderThickness" Value="0,0,1,1"/>
<Setter Property="Padding" Value="8,6"/>
</Style>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="8,4"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</DataGrid.Resources>
</DataGrid>
</Grid>
</Border>
<!-- ========== 状态栏 ========== -->
<Border Grid.Row="2"
Background="#ECEFF4"
Padding="10,6"
Margin="0,8,0,0"
CornerRadius="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding StatusMessage}"
Foreground="#444"
FontSize="12"
VerticalAlignment="Center"/>
<TextBlock Grid.Column="1"
Foreground="#666"
FontSize="12"
VerticalAlignment="Center"
Margin="8,0,0,0">
<Run Text="记录数:"/>
<Run Text="{Binding TotalCount}"/>
</TextBlock>
</Grid>
</Border>
</Grid>
</Border>
</UserControl>

View File

@@ -0,0 +1,28 @@
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 ExportModule.Views
{
/// <summary>
/// ExportView.xaml 的交互逻辑
/// </summary>
public partial class ExportView : UserControl
{
public ExportView()
{
InitializeComponent();
}
}
}