Переглянути джерело

Merge branch 'develop' of http://132.232.92.186:3000/XinXiBu/OA2023 into develop

yuanrf 3 днів тому
батько
коміт
4ccde7e27e

+ 14 - 6
OASystem/OASystem.Api/Controllers/PersonnelModuleController.cs

@@ -327,15 +327,23 @@ namespace OASystem.API.Controllers
         [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
         public async Task<IActionResult> GetWageSheetList(WageSheetListDto dto)
         {
-            //参数处理
-            string ymFormat = "yyyy-MM";
-            bool yearMonthDttIsValid = DateTime.TryParseExact(dto.YearMonth, ymFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime yearMonthDt);
-            if (!yearMonthDttIsValid)
+            if (string.IsNullOrEmpty(dto.YearMonth)) return Ok(JsonView(false, "请选择日期格式"));
+
+            //验证日期格式
+            if (!DateTime.TryParse(dto.YearMonth, out DateTime yearMonthDt))
             {
-                _result.Msg = "年月格式错误!正确时间格式:yyyy-MM  ";
-                return Ok(JsonView(false, _result.Msg));
+                return Ok(JsonView(false, "无效的日期格式"));
             }
 
+            //参数处理
+            //string ymFormat = "yyyy-MM";
+            //bool yearMonthDttIsValid = DateTime.TryParseExact(dto.YearMonth, ymFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime yearMonthDt);
+            //if (!yearMonthDttIsValid)
+            //{
+            //    _result.Msg = "年月格式错误!正确时间格式:yyyy-MM  ";
+            //    return Ok(JsonView(false, _result.Msg));
+            //}
+
             //获取月工资数据
             string yearMonth = yearMonthDt.ToString("yyyy-MM");
 

+ 553 - 1
OASystem/OASystem.Api/Controllers/SystemController.cs

@@ -1,6 +1,9 @@
 using Aspose.Cells;
+using NPOI.HPSF;
 using NPOI.POIFS.Crypt;
+using OASystem.API.OAMethodLib;
 using OASystem.Domain.AesEncryption;
+using OASystem.Domain.Attributes;
 using OASystem.Domain.Entities.Customer;
 using OASystem.Domain.Entities.Financial;
 using OASystem.Domain.Entities.Groups;
@@ -3403,7 +3406,7 @@ And u.UId = {0} And u.FId = 1 ", dto.UserId);
 
 
         /// <summary>
-        /// excel导入 策划部供应商资料
+        /// excel导入 op地接供应商资料收集模板
         /// </summary>
         /// <returns></returns>
         [HttpPost]
@@ -3928,5 +3931,554 @@ And u.UId = {0} And u.FId = 1 ", dto.UserId);
 
         #endregion
 
+        #region 资料导出Excel
+        /// <summary>
+        /// excel导出 策划部供应商资料
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> ExcelDownloadMediaSupplier()
+        {
+            var datas = await _sqlSugar.Queryable<Res_MediaSuppliers>().Where(x => x.IsDel == 0).ToListAsync();
+
+            //解密
+            var dataView = new List<MediaSupplierExcel>();
+            foreach (var item in datas)
+            {
+                EncryptionProcessor.DecryptProperties(item);
+
+                var typeName = await _sqlSugar.Queryable<Sys_SetData>()
+                    .Where(x => x.IsDel == 0 && x.Id == item.TypeId)
+                    .Select(x => x.Name)
+                    .FirstAsync();
+
+                var createUserName = await _sqlSugar.Queryable<Sys_Users>()
+                    .Where(x => x.Id == item.CreateUserId)
+                    .Select(x => x.CnName)
+                    .FirstAsync();
+
+                dataView.Add(new MediaSupplierExcel
+                {
+                    Id = item.Id,
+                    TypeName = typeName,
+                    Privince = item.Privince,
+                    City = item.City,
+                    UnitName = item.UnitName,
+                    UnitAbbreviation = item.UnitAbbreviation,
+                    UnitAddress = item.UnitAddress,
+                    Contact = item.Contact,
+                    Sex = item.Sex,
+                    Post = item.Post,
+                    Tel = item.Tel,
+                    Email = item.Email,
+                    Fax = item.Fax,
+                    CreateTime = item.CreateTime,
+                    CrateUserName = createUserName
+                });
+            }
+
+            var exportResult = await GeneralMethod.ExportToExcel<MediaSupplierExcel>(dataView, "策划部供应商资料");
+
+            return Ok(JsonView(exportResult));
+        }
+
+        private class MediaSupplierExcel
+        {
+            public int Id { get; set; }
+
+            /// <summary>
+            /// 类型
+            /// </summary>
+            public string TypeName { get; set; }
+
+
+            /// <summary>
+            /// 供应商Type(设置数据外键编号)
+            /// </summary>
+            public int TypeId { get; set; }
+
+            /// <summary>
+            /// 省份
+            /// </summary>
+            public string Privince { get; set; }
+
+            /// <summary>
+            /// 城市
+            /// </summary>
+            public string City { get; set; }
+
+            /// <summary>
+            /// 单位名称
+            /// </summary>
+            public string UnitName { get; set; }
+
+            /// <summary>
+            /// 单位缩写
+            /// </summary>
+            public string UnitAbbreviation { get; set; }
+
+            /// <summary>
+            /// 单位地址
+            /// </summary>
+            public string UnitAddress { get; set; }
+
+            /// <summary>
+            /// 单位联系人
+            /// </summary>
+            public string Contact { get; set; }
+
+            /// <summary>
+            /// 联系人性别
+            /// </summary>
+            public int Sex { get; set; }
+
+            /// <summary>
+            /// 性别
+            /// </summary>
+            public string SexRole
+            {
+                get
+                {
+                    if (Sex == 0) return "男";
+                    else if (Sex == 1) return "女";
+                    return "";
+                }
+            }
+
+            /// <summary>
+            /// 联系人职位
+            /// </summary>
+            public string Post { get; set; }
+
+            /// <summary>
+            /// 联系人电话
+            /// </summary>
+            public string Tel { get; set; }
+
+            /// <summary>
+            /// 联系人邮箱
+            /// </summary>
+            public string Email { get; set; }
+
+            /// <summary>
+            /// 联系人微信
+            /// </summary>
+            public string WeChat { get; set; }
+            /// <summary>
+            /// 传真
+            /// </summary>
+            public string Fax { get; set; }
+
+            /// <summary>
+            /// 创建人
+            /// </summary>
+            public string CrateUserName { get; set; }
+
+            /// <summary>
+            /// 创建时间
+            /// </summary>
+            public DateTime CreateTime { get; set; } = DateTime.Now;
+
+            /// <summary>
+            /// 备注
+            /// </summary>
+            public string Remark { get; set; }
+
+        }
+
+        /// <summary>
+        /// excel导出 车供应商资料
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> ExcelDownloadCar()
+        {
+            var datas = await _sqlSugar.Queryable<Res_CarData>().Where(x => x.IsDel == 0).ToListAsync();
+
+            var dataView = new List<CarDataExcel>();
+
+            var userInfos = await _sqlSugar.Queryable<Sys_Users>()
+                .Select(x => new { x.Id, x.CnName })
+                .ToListAsync();
+            foreach (var item in datas)
+            {
+                //解密
+                EncryptionProcessor.DecryptProperties(item);
+
+                var createUserName = userInfos.Find(x => x.Id == item.CreateUserId)?.CnName ?? "-";
+                var lastUpdateUserName = userInfos.Find(x => x.Id == item.LastUpdateUserId)?.CnName ?? "-";
+
+                dataView.Add(new CarDataExcel
+                {
+                    Id = item.Id,
+                    UnitArea = item.UnitArea,
+                    UnitName = item.UnitName,
+                    Address = item.Address,
+                    Contact = item.Contact,
+                    ContactTel = item.ContactTel,
+                    ContactEmail = item.ContactEmail,
+                    ContactFax = item.ContactFax,
+                    CarDes = item.CarDes,
+                    OtherInfo = item.OtherInfo,
+                    Score = item.Score,
+                    QualificationScore = item.QualificationScore,
+                    CarAgeScore = item.CarAgeScore,
+                    CleanImgScore = item.CleanImgScore,
+                    SmellScore = item.SmellScore,
+                    WaterPaperScore = item.WaterPaperScore,
+                    HardwareScore = item.HardwareScore,
+                    SafetyScore = item.SafetyScore,
+                    DrivingAgeScore = item.DrivingAgeScore,
+                    Remark = item.Remark,
+                    LastUpdateTime = item.LastUpdateTime,
+                    LastUpdateUserName = lastUpdateUserName,
+                    CreateTime = item.CreateTime,
+                    CrateUserName = createUserName
+                });
+            }
+
+            var exportResult = await GeneralMethod.ExportToExcel<CarDataExcel>(dataView, "车公司资料");
+
+            return Ok(JsonView(exportResult));
+        }
+
+        private class CarDataExcel
+        {
+            public int Id { get; set; }
+
+            /// <summary>
+            /// 区域
+            /// </summary>
+            public string UnitArea { get; set; }
+
+            /// <summary>
+            /// 公司名称
+            /// </summary>
+            public string UnitName { get; set; }
+
+            /// <summary>
+            /// 地址
+            /// </summary>
+            public string Address { get; set; }
+
+            /// <summary>
+            /// 联系人
+            /// </summary>
+            public string Contact { get; set; }
+
+            /// <summary>
+            /// 联系人手机号
+            /// </summary>
+            public string ContactTel { get; set; }
+
+            /// <summary>
+            /// 联系人邮箱
+            /// </summary>
+            public string ContactEmail { get; set; }
+
+            /// <summary>
+            /// 联系人传真
+            /// </summary>
+            public string ContactFax { get; set; }
+
+            /// <summary>
+            /// 车描述
+            /// </summary>
+            public string CarDes { get; set; }
+
+            ///// <summary>
+            ///// 车图片路径
+            ///// 存储多个 使用/r/n
+            ///// </summary>
+            //public string CarPicPaths { get; set; }
+
+            /// <summary>
+            /// 其他信息
+            /// </summary>
+            public string OtherInfo { get; set; }
+
+            /// <summary>
+            /// 服务评分
+            /// </summary>
+            public int Score { get; set; }
+
+            /// <summary>
+            /// 相关的车辆资质
+            /// A B C  选择
+            /// </summary>
+            public string QualificationScore { get; set; }
+
+            /// <summary>
+            /// 车辆2-4年新,VIP及以上需要2年新
+            /// A B C  选择
+            /// </summary>
+            public string CarAgeScore { get; set; }
+
+            /// <summary>
+            /// 车身干净,无文字图片等
+            /// A B C  选择
+            /// </summary>
+            public string CleanImgScore { get; set; }
+
+            /// <summary>
+            /// 车内整洁、无异味
+            /// A B C  选择
+            /// </summary>
+            public string SmellScore { get; set; }
+
+            /// <summary>
+            /// 提前备水,纸巾等
+            /// A B C  选择
+            /// </summary>
+            public string WaterPaperScore { get; set; }
+
+            /// <summary>
+            /// 车辆配置高(皮座椅等)
+            /// A B C  选择
+            /// </summary>
+            public string HardwareScore { get; set; }
+
+            /// <summary>
+            /// 时间概念强
+            /// A B C  选择
+            /// </summary>
+            public string TimeScore { get; set; }
+
+            /// <summary>
+            /// 安全意识高
+            /// A B C  选择
+            /// </summary>
+            public string SafetyScore { get; set; }
+
+            /// <summary>
+            /// 司机驾龄时间长,提前熟悉路线
+            /// A B C  选择
+            /// </summary>
+            public string DrivingAgeScore { get; set; }
+
+            /// <summary>
+            /// 最后更新者Id
+            /// </summary>
+            public int LastUpdateUserId { get; set; }
+
+            /// <summary>
+            /// 最后更新者
+            /// </summary>
+            public string? LastUpdateUserName { get; set; }
+
+            /// <summary>
+            ///  最后更新时间
+            /// </summary>
+            public DateTime LastUpdateTime { get; set; }
+
+            /// <summary>
+            /// 创建人
+            /// </summary>
+            public string CrateUserName { get; set; }
+
+            /// <summary>
+            /// 创建时间
+            /// </summary>
+            public DateTime CreateTime { get; set; } = DateTime.Now;
+
+            /// <summary>
+            /// 备注
+            /// </summary>
+            public string Remark { get; set; }
+
+        }
+        
+        /// <summary>
+        /// excel导出 导游地接
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost]
+        [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
+        public async Task<IActionResult> ExcelDownloadLocalGuide()
+        {
+            var datas = await _sqlSugar.Queryable<Res_LocalGuideData>().Where(x => x.IsDel == 0).ToListAsync();
+
+            var dataView = new List<LocalGuideDataExcel>();
+
+            var userInfos = await _sqlSugar.Queryable<Sys_Users>()
+                .Select(x => new { x.Id, x.CnName })
+                .ToListAsync();
+            foreach (var item in datas)
+            {
+                //解密
+                EncryptionProcessor.DecryptProperties(item);
+
+                var createUserName = userInfos.Find(x => x.Id == item.CreateUserId)?.CnName ?? "-";
+                var lastUpdateUserName = userInfos.Find(x => x.Id == item.LastUpdateUserId)?.CnName ?? "-";
+
+                dataView.Add(new LocalGuideDataExcel
+                {
+                    Id = item.Id,
+                    UnitArea = item.UnitArea,
+                    UnitName = item.UnitName,
+                    Address = item.Address,
+                    Contact = item.Contact,
+                    ContactTel = item.ContactTel,
+                    ContactEmail = item.ContactEmail,
+                    ContactFax = item.ContactFax,
+                    OtherInfo = item.OtherInfo,
+                    Score = item.Score,
+                    SuitScore = item.SuitScore,
+                    ServeScore = item.ServeScore,
+                    TalkProScore = item.TalkProScore,
+                    TimeScore = item.TimeScore,
+                    FitScore = item.FitScore,
+                    StrainScore = item.StrainScore,
+                    LocalAndChineseScore = item.LocalAndChineseScore,
+                    StaffType = item.StaffType,
+                    Remark = item.Remark,
+                    LastUpdateTime = item.LastUpdateTime,
+                    LastUpdateUserName = lastUpdateUserName,
+                    CreateTime = item.CreateTime,
+                    CrateUserName = createUserName
+                });
+            }
+
+            var exportResult = await GeneralMethod.ExportToExcel<LocalGuideDataExcel>(dataView, "导游地接资料");
+
+            return Ok(JsonView(exportResult));
+        }
+        private class LocalGuideDataExcel
+        {
+            public int Id { get; set; }
+
+            /// <summary>
+            /// 区域
+            /// </summary>
+            public string UnitArea { get; set; }
+
+            /// <summary>
+            /// 公司名称
+            /// </summary>
+            public string UnitName { get; set; }
+
+            /// <summary>
+            /// 地址
+            /// </summary>
+            public string Address { get; set; }
+
+            /// <summary>
+            /// 联系人
+            /// </summary>
+            public string Contact { get; set; }
+
+            /// <summary>
+            /// 联系人手机号
+            /// </summary>
+            public string ContactTel { get; set; }
+
+            /// <summary>
+            /// 联系人邮箱
+            /// </summary>
+            public string ContactEmail { get; set; }
+
+            /// <summary>
+            /// 联系人传真
+            /// </summary>
+            public string ContactFax { get; set; }
+
+            /// <summary>
+            /// 其他信息
+            /// </summary>
+            public string OtherInfo { get; set; }
+
+            /// <summary>
+            /// 服务评分
+            /// </summary>
+            public int Score { get; set; }
+
+            /// <summary>
+            /// 着装得体
+            /// A B C  选择
+            /// </summary>
+            public string SuitScore { get; set; }
+
+            /// <summary>
+            /// 服务意识强度
+            /// A B C  选择
+            /// </summary>
+            public string ServeScore { get; set; }
+
+            /// <summary>
+            /// 讲解水平专业
+            /// A B C  选择
+            /// </summary>
+            public string TalkProScore { get; set; }
+
+            /// <summary>
+            /// 时间概念强度
+            /// A B C  选择
+            /// </summary>
+            public string TimeScore { get; set; }
+
+            /// <summary>
+            /// 配合能力强,服从安排
+            /// A B C  选择
+            /// </summary>
+            public string FitScore { get; set; }
+
+            /// <summary>
+            /// 应变能力强
+            /// A B C  选择
+            /// </summary>
+            public string StrainScore { get; set; }
+
+            /// <summary>
+            /// 当地语言和中文表达流畅
+            /// A B C  选择
+            /// </summary>
+            public string LocalAndChineseScore { get; set; }
+
+            /// <summary>
+            /// 导游地接的类型:0公司1私人
+            /// </summary>
+            [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+            public int StaffType { get; set; }
+
+            public string StaffTypeLabel { get{
+
+                    if (StaffType == 1) return "私人";
+                    else if (StaffType == 0) return "公司";
+                    return "-";
+                } }
+
+            /// 最后更新者Id
+            /// </summary>
+            public int LastUpdateUserId { get; set; }
+
+            /// <summary>
+            /// 最后更新者
+            /// </summary>
+            public string? LastUpdateUserName { get; set; }
+
+            /// <summary>
+            ///  最后更新时间
+            /// </summary>
+            public DateTime LastUpdateTime { get; set; }
+
+            /// <summary>
+            /// 创建人
+            /// </summary>
+            public string CrateUserName { get; set; }
+
+            /// <summary>
+            /// 创建时间
+            /// </summary>
+            public DateTime CreateTime { get; set; } = DateTime.Now;
+
+            /// <summary>
+            /// 备注
+            /// </summary>
+            public string Remark { get; set; }
+
+        }
+        #endregion
     }
 }

+ 393 - 0
OASystem/OASystem.Api/OAMethodLib/GeneralMethod.cs

@@ -5777,6 +5777,399 @@ namespace OASystem.API.OAMethodLib
         #endregion
         #endregion
 
+        
+        #region Excel导出服务
+        #region 类
+        public class FileExportSettings
+        {
+            public string ExportBasePath { get; set; } = "wwwroot/exports";
+            public string DownloadBaseUrl { get; set; } = "/exports";
+            public int RetentionDays { get; set; } = 7;
+            public int MaxFileSizeMB { get; set; } = 50;
+            public string ExcelTemplatesPath { get; set; } = "Templates/Excel";
+        }
+
+        public class ExportResult
+        {
+            public bool Success { get; set; }
+            public string FilePath { get; set; }
+            public string DownloadUrl { get; set; }
+            public string FileName { get; set; }
+            public long FileSize { get; set; }
+            public string MimeType { get; set; } = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+            public string ErrorMessage { get; set; }
+            public DateTime GeneratedAt { get; set; } = DateTime.Now;
+        }
+
+        public class ExportRequest
+        {
+            public IEnumerable<object> Data { get; set; }
+            public string FileName { get; set; }
+            public string SheetName { get; set; } = "Sheet1";
+            public string TemplateType { get; set; } = "default";
+            public Dictionary<string, string> CustomHeaders { get; set; }
+        }
+
+        public class ExportResponse
+        {
+            public string FilePath { get; set; }
+            public string DownloadUrl { get; set; }
+            public string FileName { get; set; }
+            public long FileSize { get; set; }
+            public DateTime GeneratedAt { get; set; }
+            public string Message { get; set; }
+        }
+        #endregion
+
+
+
+        public static async Task<ExportResult> ExportToExcel<T>(IEnumerable<T> data, string fileName, string sheetName = "Sheet1", string templateType = "default")
+        {
+            try
+            {
+                byte[] excelBytes = templateType.ToLower() switch
+                {
+                    "styled" => GenerateStyledExcel(data, sheetName),
+                    "with_formulas" => GenerateExcelWithFormulas(data, sheetName),
+                    //"with_charts" => GenerateExcelWithCharts(data, sheetName),
+                    _ => GenerateDefaultExcel(data, sheetName)
+                };
+
+                var filePath = await SaveFileAsync(excelBytes, fileName, "excel");
+                var downloadUrl = $"wwwroot/exports/{filePath}";
+
+                return new ExportResult
+                {
+                    Success = true,
+                    FilePath = filePath,
+                    DownloadUrl = downloadUrl,
+                    FileName = Path.GetFileName(filePath),
+                    FileSize = excelBytes.Length,
+                    MimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+                };
+            }
+            catch (Exception ex)
+            {
+                return new ExportResult
+                {
+                    Success = false,
+                    ErrorMessage = ex.Message
+                };
+            }
+        }
+
+        public static async Task<ExportResult> ExportWithTemplate<T>(IEnumerable<T> data, byte[] templateBytes, string fileName, string sheetName = "Sheet1")
+        {
+            try
+            {
+                using var templateStream = new MemoryStream(templateBytes);
+                using var workbook = new Workbook(templateStream);
+
+                var worksheet = workbook.Worksheets[sheetName] ?? workbook.Worksheets[0];
+
+                // 查找数据起始行(可以根据模板中的标记来定位)
+                int startRow = FindDataStartRow(worksheet);
+
+                // 填充数据
+                FillWorksheetWithData(worksheet, data, startRow);
+
+                using var outputStream = new MemoryStream();
+                workbook.Save(outputStream, Aspose.Cells.SaveFormat.Xlsx);
+                var excelBytes = outputStream.ToArray();
+
+                var filePath = await SaveFileAsync(excelBytes, fileName, "excel/templates");
+                var downloadUrl = $"wwwroot/exports/exports/{filePath}";
+
+                return new ExportResult
+                {
+                    Success = true,
+                    FilePath = filePath,
+                    DownloadUrl = downloadUrl,
+                    FileName = Path.GetFileName(filePath),
+                    FileSize = excelBytes.Length,
+                    MimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+                };
+            }
+            catch (Exception ex)
+            {
+                return new ExportResult
+                {
+                    Success = false,
+                    ErrorMessage = ex.Message
+                };
+            }
+        }
+
+        public static async Task<ExportResult> ExportMultipleSheets<T>(Dictionary<string, IEnumerable<T>> sheetsData, string fileName)
+        {
+            try
+            {
+                using var workbook = new Workbook();
+                workbook.Worksheets.Clear(); // 清除默认工作表
+
+                foreach (var sheet in sheetsData)
+                {
+                    var worksheet = workbook.Worksheets.Add(sheet.Key);
+                    var data = sheet.Value;
+
+                    if (data != null && data.Any())
+                    {
+                        FillWorksheetWithData(worksheet, data, 0);
+                        worksheet.AutoFitColumns();
+                    }
+                }
+
+                using var stream = new MemoryStream();
+                workbook.Save(stream, Aspose.Cells.SaveFormat.Xlsx);
+                var excelBytes = stream.ToArray();
+
+                var filePath = await SaveFileAsync(excelBytes, fileName, "excel/multi-sheets");
+                var downloadUrl = $"wwwroot/exports/exports/{filePath}";
+
+                return new ExportResult
+                {
+                    Success = true,
+                    FilePath = filePath,
+                    DownloadUrl = downloadUrl,
+                    FileName = Path.GetFileName(filePath),
+                    FileSize = excelBytes.Length,
+                    MimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+                };
+            }
+            catch (Exception ex)
+            {
+                return new ExportResult
+                {
+                    Success = false,
+                    ErrorMessage = ex.Message
+                };
+            }
+        }
+
+        public static async Task<ExportResult> ExportWithCustomHeaders<T>(IEnumerable<T> data, Dictionary<string, string> headers, string fileName, string sheetName = "Sheet1")
+        {
+            try
+            {
+                using var workbook = new Workbook();
+                var worksheet = workbook.Worksheets[0];
+                worksheet.Name = sheetName;
+
+                // 设置自定义表头
+                int colIndex = 0;
+                foreach (var header in headers)
+                {
+                    worksheet.Cells[0, colIndex].PutValue(header.Value);
+                    colIndex++;
+                }
+
+                // 填充数据
+                if (data != null && data.Any())
+                {
+                    int rowIndex = 1;
+                    var properties = typeof(T).GetProperties();
+
+                    foreach (var item in data)
+                    {
+                        colIndex = 0;
+                        foreach (var header in headers)
+                        {
+                            var property = properties.FirstOrDefault(p => p.Name == header.Key);
+                            if (property != null)
+                            {
+                                var value = property.GetValue(item);
+                                worksheet.Cells[rowIndex, colIndex].PutValue(value);
+                            }
+                            colIndex++;
+                        }
+                        rowIndex++;
+                    }
+                }
+
+                worksheet.AutoFitColumns();
+
+                using var stream = new MemoryStream();
+                workbook.Save(stream, Aspose.Cells.SaveFormat.Xlsx);
+                var excelBytes = stream.ToArray();
+
+                var filePath = await SaveFileAsync(excelBytes, fileName, "excel/custom-headers");
+                var downloadUrl = $"filePath";
+
+                return new ExportResult
+                {
+                    Success = true,
+                    FilePath = filePath,
+                    DownloadUrl = downloadUrl,
+                    FileName = Path.GetFileName(filePath),
+                    FileSize = excelBytes.Length,
+                    MimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+                };
+            }
+            catch (Exception ex)
+            {
+                return new ExportResult
+                {
+                    Success = false,
+                    ErrorMessage = ex.Message
+                };
+            }
+        }
+
+        private static byte[] GenerateDefaultExcel<T>(IEnumerable<T> data, string sheetName)
+        {
+            using var workbook = new Workbook();
+            var worksheet = workbook.Worksheets[0];
+            worksheet.Name = sheetName;
+
+            if (data != null && data.Any())
+            {
+                FillWorksheetWithData(worksheet, data, 0);
+                worksheet.AutoFitColumns();
+            }
+
+            return SaveWorkbookToBytes(workbook);
+        }
+
+        private static byte[] GenerateStyledExcel<T>(IEnumerable<T> data, string sheetName)
+        {
+            using var workbook = new Workbook();
+            var worksheet = workbook.Worksheets[0];
+            worksheet.Name = sheetName;
+
+            if (data != null && data.Any())
+            {
+                FillWorksheetWithData(worksheet, data, 0);
+
+                // 设置样式
+                var headerStyle = workbook.CreateStyle();
+                headerStyle.Pattern = BackgroundType.Solid;
+                headerStyle.ForegroundColor = System.Drawing.Color.LightGray;
+                headerStyle.Font.IsBold = true;
+                headerStyle.HorizontalAlignment = TextAlignmentType.Center;
+
+                var properties = typeof(T).GetProperties();
+                Aspose.Cells.Range headerRange = worksheet.Cells.CreateRange(0, 0, 1, properties.Length);
+                headerRange.ApplyStyle(headerStyle, new StyleFlag { All = true });
+
+                worksheet.AutoFitColumns();
+            }
+
+            return SaveWorkbookToBytes(workbook);
+        }
+
+        private static byte[] GenerateExcelWithFormulas<T>(IEnumerable<T> data, string sheetName)
+        {
+            using var workbook = new Workbook();
+            var worksheet = workbook.Worksheets[0];
+            worksheet.Name = sheetName;
+
+            if (data != null && data.Any())
+            {
+                FillWorksheetWithData(worksheet, data, 0);
+
+                // 添加公式(例如:求和公式)
+                var properties = typeof(T).GetProperties();
+                int lastRow = data.Count() + 1;
+
+                for (int i = 0; i < properties.Length; i++)
+                {
+                    if (IsNumericType(properties[i].PropertyType))
+                    {
+                        worksheet.Cells[lastRow, i].Formula = $"=SUM({worksheet.Cells[1, i].Name}:{worksheet.Cells[lastRow - 1, i].Name})";
+                    }
+                }
+
+                worksheet.AutoFitColumns();
+            }
+
+            return SaveWorkbookToBytes(workbook);
+        }
+
+        private static void FillWorksheetWithData<T>(Worksheet worksheet, IEnumerable<T> data, int startRow)
+        {
+            var properties = typeof(T).GetProperties();
+
+            // 设置表头
+            for (int i = 0; i < properties.Length; i++)
+            {
+                worksheet.Cells[startRow, i].PutValue(properties[i].Name);
+            }
+
+            // 填充数据
+            int rowIndex = startRow + 1;
+            foreach (var item in data)
+            {
+                for (int colIndex = 0; colIndex < properties.Length; colIndex++)
+                {
+                    var value = properties[colIndex].GetValue(item);
+                    worksheet.Cells[rowIndex, colIndex].PutValue(value);
+                }
+                rowIndex++;
+            }
+        }
+
+        private static int FindDataStartRow(Worksheet worksheet)
+        {
+            // 在实际项目中,可以根据模板中的特定标记来定位数据起始行
+            // 例如查找 "{{DATA_START}}" 这样的标记
+            for (int row = 0; row < 50; row++)
+            {
+                for (int col = 0; col < 10; col++)
+                {
+                    var value = worksheet.Cells[row, col].StringValue;
+                    if (value == "{{DATA_START}}")
+                    {
+                        worksheet.Cells[row, col].PutValue(""); // 清空标记
+                        return row + 1;
+                    }
+                }
+            }
+            return 1; // 默认从第2行开始
+        }
+
+        private static byte[] SaveWorkbookToBytes(Workbook workbook)
+        {
+            using var stream = new MemoryStream();
+            workbook.Save(stream, Aspose.Cells.SaveFormat.Xlsx);
+            return stream.ToArray();
+        }
+
+        private static bool IsNumericType(Type type)
+        {
+            return Type.GetTypeCode(type) switch
+            {
+                TypeCode.Byte or TypeCode.SByte or TypeCode.UInt16 or TypeCode.UInt32 or
+                TypeCode.UInt64 or TypeCode.Int16 or TypeCode.Int32 or TypeCode.Int64 or
+                TypeCode.Decimal or TypeCode.Double or TypeCode.Single => true,
+                _ => false
+            };
+        }
+
+
+        public static async Task<string> SaveFileAsync(byte[] fileData, string fileName, string subFolder = "")
+        {
+            try
+            {
+                var basePath ="wwwroot/exports/exports";
+                var folderPath = Path.Combine(basePath, subFolder);
+                Directory.CreateDirectory(folderPath);
+
+                var uniqueFileName = $"{Path.GetFileNameWithoutExtension(fileName)}_{Guid.NewGuid():N}{Path.GetExtension(fileName)}.xlsx";
+                var filePath = Path.Combine(folderPath, uniqueFileName);
+
+                await System.IO.File.WriteAllBytesAsync(filePath, fileData);
+
+                var relativePath = Path.Combine(basePath, subFolder, uniqueFileName).Replace("\\", "/");
+
+                return relativePath;
+            }
+            catch (Exception ex)
+            {
+                throw;
+            }
+        }
+
+        #endregion
+
     }
 }
 

+ 8 - 0
OASystem/OASystem.Domain/Entities/Groups/Grp_CreditCardPayment.cs

@@ -92,41 +92,49 @@ namespace OASystem.Domain.Entities.Groups
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(125)")]
         public string OtherSideName { get; set; }
+
         /// <summary>
         /// 财务操作人 用户Id
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "int")]
         public int MFOperator { get; set; }
+
         /// <summary>
         /// 财务操作时间
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string MFOperatorDate { get; set; }
+
         /// <summary>
         /// 部门经理是否审核 0否1是
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "int")]
         public int IsAuditDM { get; set; }
+
         /// <summary>
         /// 部门经理审核人 用户Id
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "int")]
         public int AuditDMOperate { get; set; }
+
         /// <summary>
         /// 部门经理审核时间
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "varchar(30)")]
         public string AuditDMDate { get; set; }
+
         /// <summary>
         /// 财务部是否审核  0否1是
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "int")]
         public int IsAuditMF { get; set; }
+
         /// <summary>
         /// 财务部审核人
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "int")]
         public int AuditMFOperate { get; set; }
+
         /// <summary>
         /// 财务部审核时间
         /// </summary>

+ 1 - 0
OASystem/OASystem.Domain/Entities/Groups/Grp_DecreasePayments.cs

@@ -37,6 +37,7 @@ namespace OASystem.Domain.Entities.Groups
         /// 供应商名称
         /// </summary>
         [SugarColumn(IsNullable = true, ColumnDataType = "nvarchar(200)")]
+
         public string SupplierName { get; set; }
 
         /// <summary>