251112
This commit is contained in:
@@ -115,6 +115,7 @@ namespace FATrace.WPLApp
|
||||
var fsql = Container.Resolve<IFreeSql>();
|
||||
var sysRun = Container.Resolve<SysRunService>();
|
||||
var DataServices = Container.Resolve<DataServices>();
|
||||
var CsvServices = Container.Resolve<CsvServices>();
|
||||
LogService.Info("Background services initialized");
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -197,8 +198,9 @@ namespace FATrace.WPLApp
|
||||
//containerRegistry.RegisterSingleton<BatchControlService>();
|
||||
//containerRegistry.RegisterSingleton<TankService>();
|
||||
//containerRegistry.RegisterSingleton<MachineRtDataService>();
|
||||
|
||||
|
||||
containerRegistry.RegisterSingleton<DataServices>();
|
||||
containerRegistry.RegisterSingleton<CsvServices>();
|
||||
containerRegistry.RegisterSingleton<NavigationServices>();
|
||||
|
||||
|
||||
|
||||
89
FATrace.WPLApp/ModelDto/RawProUserCsvDto.cs
Normal file
89
FATrace.WPLApp/ModelDto/RawProUserCsvDto.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FATrace.WPLApp.ModelDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 原料生产使用信息 CSV DTO
|
||||
/// </summary>
|
||||
public class RawProUserCsvDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 原料编号
|
||||
/// </summary>
|
||||
public string? RawCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 原料名称
|
||||
/// </summary>
|
||||
public string? RawName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 内袋二维码
|
||||
/// </summary>
|
||||
public string? InBagCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 外箱二维码
|
||||
/// </summary>
|
||||
public string? BoxCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 批号
|
||||
/// </summary>
|
||||
public string? Batch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 保质期 年
|
||||
/// </summary>
|
||||
public double ShelfLife { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 称重重量 g 克
|
||||
/// </summary>
|
||||
public double Weight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 配料日期 当天日期
|
||||
/// 年,月,日
|
||||
/// </summary>
|
||||
public string? DeliveryDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 剩余重量 g 克
|
||||
/// 剩余重量 = 当前产品的入库总重量-当前称重的称量重量
|
||||
/// </summary>
|
||||
public double RemainWeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 入库总重量
|
||||
/// 当前的入库的总重量
|
||||
/// </summary>
|
||||
public double StockWeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 称重时间
|
||||
/// </summary>
|
||||
public DateTime WeightTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作者
|
||||
/// </summary>
|
||||
public string? OpUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 确认者
|
||||
/// </summary>
|
||||
public string? CheckUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 出库时间
|
||||
/// 外箱扫码出库时间
|
||||
/// </summary>
|
||||
public DateTime OutTime { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,135 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration;
|
||||
using FATrace.WPLApp.ModelDto;
|
||||
using Prism.Mvvm;
|
||||
|
||||
namespace FATrace.WPLApp.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// CSV服务
|
||||
///导出CSV文件
|
||||
/// </summary>
|
||||
public class CsvServices
|
||||
public class CsvServices:BindableBase
|
||||
{
|
||||
public CsvServices()
|
||||
public ILogService LogService { get; }
|
||||
public IFreeSql FreeSql { get; }
|
||||
|
||||
public CsvServices(ILogService logService, IFreeSql freeSql)
|
||||
{
|
||||
|
||||
LogService = logService;
|
||||
FreeSql = freeSql;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出单条记录为 CSV 文件,文件名即记录的 InBagCode(自动追加 .csv 扩展名)。
|
||||
/// </summary>
|
||||
/// <param name="item">要导出的数据项,类型为 RawProUserCsvDto</param>
|
||||
/// <param name="outputDirectory">导出目录,不存在将自动创建</param>
|
||||
/// <param name="overwrite">若目标文件已存在,是否允许覆盖</param>
|
||||
/// <returns>导出后的完整文件路径</returns>
|
||||
/// <exception cref="ArgumentNullException">item 或 outputDirectory 为 null</exception>
|
||||
/// <exception cref="ArgumentException">InBagCode 为空或仅空白</exception>
|
||||
/// <exception cref="IOException">文件已存在且不允许覆盖</exception>
|
||||
public string ExportSingle(RawProUserCsvDto item, string outputDirectory, bool overwrite = false)
|
||||
{
|
||||
if (item is null) throw new ArgumentNullException(nameof(item));
|
||||
if (outputDirectory is null) throw new ArgumentNullException(nameof(outputDirectory));
|
||||
|
||||
var inBagCode = (item.InBagCode ?? string.Empty).Trim();
|
||||
if (string.IsNullOrWhiteSpace(inBagCode))
|
||||
throw new ArgumentException("InBagCode 不能为空", nameof(item.InBagCode));
|
||||
|
||||
// 目录确保存在
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
|
||||
var safeName = SanitizeFileName(inBagCode);
|
||||
var filePath = Path.Combine(outputDirectory, safeName + ".csv");
|
||||
|
||||
if (File.Exists(filePath) && !overwrite)
|
||||
{
|
||||
throw new IOException($"文件已存在且不允许覆盖: {filePath}");
|
||||
}
|
||||
|
||||
// 使用 UTF8 BOM,便于 Excel 正确识别中文
|
||||
using var writer = new StreamWriter(filePath, false, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
|
||||
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
|
||||
|
||||
// 固定列顺序映射,并设置日期格式
|
||||
csv.Context.RegisterClassMap<RawProUserCsvDtoMap>();
|
||||
|
||||
csv.WriteHeader<RawProUserCsvDto>();
|
||||
csv.NextRecord();
|
||||
csv.WriteRecord(item);
|
||||
csv.NextRecord();
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量导出:把每一条记录分别导出为以各自 InBagCode 命名的 CSV 文件。
|
||||
/// </summary>
|
||||
/// <param name="items">数据集合</param>
|
||||
/// <param name="outputDirectory">导出目录,不存在将自动创建</param>
|
||||
/// <param name="overwrite">若目标文件已存在,是否允许覆盖</param>
|
||||
/// <returns>成功导出的文件完整路径集合</returns>
|
||||
public IEnumerable<string> ExportMany(IEnumerable<RawProUserCsvDto> items, string outputDirectory, bool overwrite = false)
|
||||
{
|
||||
if (items is null) throw new ArgumentNullException(nameof(items));
|
||||
|
||||
var results = new List<string>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
// 逐条导出,沿用相同规则
|
||||
var path = ExportSingle(item, outputDirectory, overwrite);
|
||||
results.Add(path);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理文件名中的非法字符。
|
||||
/// </summary>
|
||||
private static string SanitizeFileName(string name)
|
||||
{
|
||||
var invalid = Path.GetInvalidFileNameChars();
|
||||
var sb = new StringBuilder(name.Length);
|
||||
foreach (var ch in name)
|
||||
{
|
||||
sb.Append(invalid.Contains(ch) ? '_' : ch);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RawProUserCsvDto 的 CSV 列映射,固定输出顺序并设置日期格式。
|
||||
/// </summary>
|
||||
private sealed class RawProUserCsvDtoMap : ClassMap<RawProUserCsvDto>
|
||||
{
|
||||
public RawProUserCsvDtoMap()
|
||||
{
|
||||
Map(x => x.RawCode).Index(0).Name(nameof(RawProUserCsvDto.RawCode));
|
||||
Map(x => x.RawName).Index(1).Name(nameof(RawProUserCsvDto.RawName));
|
||||
Map(x => x.InBagCode).Index(2).Name(nameof(RawProUserCsvDto.InBagCode));
|
||||
Map(x => x.BoxCode).Index(3).Name(nameof(RawProUserCsvDto.BoxCode));
|
||||
Map(x => x.Batch).Index(4).Name(nameof(RawProUserCsvDto.Batch));
|
||||
Map(x => x.ShelfLife).Index(5).Name(nameof(RawProUserCsvDto.ShelfLife));
|
||||
Map(x => x.Weight).Index(6).Name(nameof(RawProUserCsvDto.Weight));
|
||||
Map(x => x.DeliveryDate).Index(7).Name(nameof(RawProUserCsvDto.DeliveryDate));
|
||||
Map(x => x.RemainWeight).Index(8).Name(nameof(RawProUserCsvDto.RemainWeight));
|
||||
Map(x => x.StockWeight).Index(9).Name(nameof(RawProUserCsvDto.StockWeight));
|
||||
Map(x => x.WeightTime).Index(10).Name(nameof(RawProUserCsvDto.WeightTime)).TypeConverterOption.Format("yyyy-MM-dd HH:mm:ss");
|
||||
Map(x => x.OpUser).Index(11).Name(nameof(RawProUserCsvDto.OpUser));
|
||||
Map(x => x.CheckUser).Index(12).Name(nameof(RawProUserCsvDto.CheckUser));
|
||||
Map(x => x.OutTime).Index(13).Name(nameof(RawProUserCsvDto.OutTime)).TypeConverterOption.Format("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,10 @@
|
||||
Width="100"
|
||||
Command="{Binding ExportCommand}"
|
||||
Content="导出Excel" />
|
||||
<Button
|
||||
Width="100"
|
||||
Command="{Binding ExportCSVCommand}"
|
||||
Content="导出CSV" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
Reference in New Issue
Block a user