using System.IO; using System.Windows; using Autodesk.Revit.DB; using Melskin.Controls; using MiniExcelLibs; using Nice3point.Revit.Toolkit.External; using ShrlAlgoToolkit.RevitAddins.Common.Assists; namespace ShrlAlgoToolkit.RevitAddins.DrawSheet; [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)] public class ExportSchedulesCmd : ExternalCommand { public override void Execute() { var list = Document.OfCollector().OfClass(typeof(ViewSchedule)).OfCategory(BuiltInCategory.OST_Schedules).Cast().ToList(); if (!list.Any()) { ErrorMessage = "当前项目不存在明细表"; Result = Result.Failed; return; } var browserWindow = new VistaFolderBrowserDialog(); browserWindow.ShowDialog(); var selectedPath = browserWindow.SelectedPath; if (selectedPath == null || !Directory.Exists(selectedPath)) { //var manager = new NotificationMessageManager(); //manager.CreateMessage().Primary("#1751C3").Background("#333").HasBadge("净高").HasMessage("未选择文件夹").Queue(); Result = Result.Cancelled; return; } var path = $"{selectedPath}\\导出的明细表.xlsx"; ExportScheduleToExcel(list, path); WinDialogAssist.OpenFolderAndSelectFile(path); } public void ExportScheduleToExcel(List list,string path) { var allSheetsData = new Dictionary(); foreach (var viewSchedule in list) { // 过滤掉无法分配类型的明细表(如修订云线明细表等特殊情况) if (!viewSchedule.CanHaveTypeAssigned()) continue; var td = viewSchedule.GetTableData(); var tdd = td.GetSectionData(SectionType.Body); var nColumns = tdd.NumberOfColumns; var nRows = tdd.NumberOfRows; // 如果明细表连标题行都没有,直接跳过 if (nRows == 0) continue; // 1. 获取第 0 行作为 Excel 的标题 (Headers) var headers = new List(); for (int j = 0; j < nColumns; j++) { string headerName = viewSchedule.GetCellText(SectionType.Body, 0, j); // 容错处理:如果列名为空,给个默认名;如果列名重复,加后缀(MiniExcel要求Key唯一) if (string.IsNullOrWhiteSpace(headerName)) headerName = $"Column_{j + 1}"; string uniqueName = headerName; int counter = 1; while (headers.Contains(uniqueName)) { uniqueName = $"{headerName}_{counter++}"; } headers.Add(uniqueName); } // 2. 构造数据行(从第 1 行开始循环) var rowsData = new List>(); for (int i = 1; i < nRows; i++) { var row = new Dictionary(); for (int j = 0; j < nColumns; j++) { // 使用上面取到的 headers[j] 作为 Key row[headers[j]] = viewSchedule.GetCellText(SectionType.Body, i, j); } rowsData.Add(row); } // 3. 处理 Sheet 名称非法字符 (Excel 不允许 \ / ? * [ ] : ) string safeSheetName = viewSchedule.Name; char[] invalidChars = { '\\', '/', '?', '*', '[', ']', ':' }; foreach (var c in invalidChars) safeSheetName = safeSheetName.Replace(c, '_'); if (safeSheetName.Length > 31) safeSheetName = safeSheetName.Substring(0, 31); allSheetsData.Add(safeSheetName, rowsData); } try { MiniExcel.SaveAs(path, allSheetsData, overwriteFile: true); } catch (IOException) { // 提示文件被占用 MessageBox.Show("请先关闭已打开的 Excel 文件后再尝试保存。"); } } }