using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using OfficeOpenXml; namespace ThriftPipelineTools { /// /// Excel 查看器 - 用于学习和测试 EPPlus 的 Excel 读取功能 /// /// 功能: /// 1. 选择 Excel 文件 (.xlsx) /// 2. 加载并显示所有工作表 (Sheet) /// 3. 点击工作表查看数据内容 /// 4. 实时预览 Excel 数据结构 /// /// 学习目的: /// - 理解 EPPlus 的基本用法 /// - 了解 Excel 文件的结构(Workbook、Worksheet、Cells) /// - 掌握单元格数据的读取方法 /// public class EPPlusTestEditor : EditorWindow { // ========== 文件路径 ========== private string excelFilePath = ""; // 选中的Excel文件路径 // ========== Excel 数据 ========== private List sheetNames = new List(); // 所有工作表名称 private int selectedSheetIndex = 0; // 当前选中的工作表索引 private string[,] sheetData = null; // 当前工作表的数据(二维数组) private int maxRows = 0; // 当前工作表的最大行数 private int maxCols = 0; // 当前工作表的最大列数 // ========== UI 状态 ========== private Vector2 sheetListScrollPosition; // 工作表列表滚动位置 private Vector2 dataScrollPosition; // 数据表格滚动位置 private bool isFileLoaded = false; // 是否已加载文件 private string statusMessage = "请选择一个 Excel 文件"; // 状态消息 // ========== UI 配置 ========== private const float CELL_WIDTH = 120f; // 单元格显示宽度 private const float CELL_HEIGHT = 20f; // 单元格显示高度 private const int MAX_DISPLAY_ROWS = 100; // 最多显示行数(避免性能问题) private const int MAX_DISPLAY_COLS = 50; // 最多显示列数 /// /// Unity 菜单项:打开 Excel 查看器窗口 /// [MenuItem("配置表pipeline/Thrift/Excel 查看器")] public static void ShowWindow() { var window = GetWindow("Excel 查看器"); window.minSize = new Vector2(800, 600); window.Show(); } /// /// 绘制 Editor 窗口 UI /// private void OnGUI() { EditorGUILayout.Space(10); // ===== 标题区域 ===== EditorGUILayout.LabelField("EPPlus Excel 读取测试工具", EditorStyles.boldLabel); EditorGUILayout.HelpBox( "此工具用于测试 EPPlus 读取 Excel 文件的功能\n" + "1. 选择 .xlsx 文件\n" + "2. 点击「加载文件」查看所有工作表\n" + "3. 选择工作表查看数据内容", MessageType.Info ); EditorGUILayout.Space(10); // ===== 文件选择区域 ===== DrawFileSelection(); EditorGUILayout.Space(10); // ===== 状态信息 ===== EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("状态:", statusMessage, EditorStyles.miniLabel); if (isFileLoaded) { EditorGUILayout.LabelField($"工作表数量: {sheetNames.Count}", EditorStyles.miniLabel); if (sheetData != null) { EditorGUILayout.LabelField($"当前表格: {maxRows} 行 × {maxCols} 列", EditorStyles.miniLabel); } } EditorGUILayout.EndVertical(); EditorGUILayout.Space(10); // ===== 主内容区域 ===== if (isFileLoaded) { DrawMainContent(); } } /// /// 绘制文件选择区域 /// private void DrawFileSelection() { EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("Excel 文件选择", EditorStyles.boldLabel); // 文件路径输入框和浏览按钮 EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("文件路径:", GUILayout.Width(70)); excelFilePath = EditorGUILayout.TextField(excelFilePath); if (GUILayout.Button("浏览...", GUILayout.Width(70))) { string selected = EditorUtility.OpenFilePanel("选择 Excel 文件", "", "xlsx"); if (!string.IsNullOrEmpty(selected)) { excelFilePath = selected; } } EditorGUILayout.EndHorizontal(); // 加载按钮 EditorGUILayout.BeginHorizontal(); GUI.enabled = !string.IsNullOrEmpty(excelFilePath) && File.Exists(excelFilePath); if (GUILayout.Button("🔄 加载文件", GUILayout.Height(30))) { LoadExcelFile(); } // 刷新按钮(仅在已加载时显示) if (isFileLoaded) { if (GUILayout.Button("🔃 刷新数据", GUILayout.Height(30))) { LoadExcelFile(); } } GUI.enabled = true; EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } /// /// 绘制主要内容区域(工作表列表 + 数据表格) /// private void DrawMainContent() { EditorGUILayout.BeginHorizontal(); // ===== 左侧:工作表列表 ===== EditorGUILayout.BeginVertical("box", GUILayout.Width(200)); EditorGUILayout.LabelField("工作表列表", EditorStyles.boldLabel); sheetListScrollPosition = EditorGUILayout.BeginScrollView( sheetListScrollPosition, GUILayout.ExpandHeight(true) ); for (int i = 0; i < sheetNames.Count; i++) { // 高亮显示选中的工作表 bool isSelected = (i == selectedSheetIndex); GUI.backgroundColor = isSelected ? Color.cyan : Color.white; if (GUILayout.Button($"📄 {sheetNames[i]}", GUILayout.Height(25))) { selectedSheetIndex = i; LoadSheetData(i); } GUI.backgroundColor = Color.white; } EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); // ===== 右侧:数据表格 ===== EditorGUILayout.BeginVertical("box", GUILayout.ExpandWidth(true)); if (sheetData != null) { EditorGUILayout.LabelField( $"工作表: {sheetNames[selectedSheetIndex]}", EditorStyles.boldLabel ); DrawDataTable(); } else { EditorGUILayout.LabelField("请选择一个工作表查看数据", EditorStyles.centeredGreyMiniLabel); } EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); } /// /// 绘制数据表格 /// private void DrawDataTable() { if (sheetData == null || maxRows == 0 || maxCols == 0) return; dataScrollPosition = EditorGUILayout.BeginScrollView( dataScrollPosition, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true) ); // 限制显示的行列数 int displayRows = Mathf.Min(maxRows, MAX_DISPLAY_ROWS); int displayCols = Mathf.Min(maxCols, MAX_DISPLAY_COLS); // 提示信息(如果数据被截断) if (maxRows > MAX_DISPLAY_ROWS || maxCols > MAX_DISPLAY_COLS) { EditorGUILayout.HelpBox( $"数据过大,仅显示前 {displayRows} 行 × {displayCols} 列", MessageType.Warning ); } // 使用 GUILayout 绘制表格 EditorGUILayout.BeginVertical(); for (int row = 0; row < displayRows; row++) { EditorGUILayout.BeginHorizontal(); // 行号 EditorGUILayout.LabelField( $"{row + 1}", GUILayout.Width(40) ); // 每列的数据 for (int col = 0; col < displayCols; col++) { string cellValue = sheetData[row, col] ?? ""; // 第一行(表头)使用不同样式 if (row == 0) { GUI.backgroundColor = new Color(0.8f, 0.9f, 1f); EditorGUILayout.LabelField( cellValue, EditorStyles.boldLabel, GUILayout.Width(CELL_WIDTH), GUILayout.Height(CELL_HEIGHT) ); GUI.backgroundColor = Color.white; } else { EditorGUILayout.LabelField( cellValue, GUILayout.Width(CELL_WIDTH), GUILayout.Height(CELL_HEIGHT) ); } } EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); EditorGUILayout.EndScrollView(); } /// /// 加载 Excel 文件,获取所有工作表名称 /// private void LoadExcelFile() { try { if (!File.Exists(excelFilePath)) { statusMessage = "❌ 文件不存在!"; Debug.LogError($"文件不存在: {excelFilePath}"); return; } // 清空之前的数据 sheetNames.Clear(); sheetData = null; selectedSheetIndex = 0; // 使用 EPPlus 读取 Excel FileInfo fileInfo = new FileInfo(excelFilePath); using (ExcelPackage package = new ExcelPackage(fileInfo)) { // 获取所有工作表名称 // 注意:使用名称访问更可靠,Worksheets集合的索引可能不是从0开始 Debug.Log($"[EPPlus测试] Worksheets.Count = {package.Workbook.Worksheets.Count}"); foreach (ExcelWorksheet worksheet in package.Workbook.Worksheets) { sheetNames.Add(worksheet.Name); Debug.Log($"[EPPlus测试] 发现工作表: '{worksheet.Name}'"); } if (sheetNames.Count == 0) { statusMessage = "⚠️ Excel 文件中没有工作表"; isFileLoaded = false; return; } isFileLoaded = true; statusMessage = $"✅ 成功加载文件,共 {sheetNames.Count} 个工作表"; // 自动加载第一个工作表 LoadSheetData(0); Debug.Log($"[EPPlus测试] 成功加载: {Path.GetFileName(excelFilePath)}"); Debug.Log($"[EPPlus测试] 工作表列表: {string.Join(", ", sheetNames)}"); } } catch (Exception ex) { statusMessage = $"❌ 加载失败: {ex.Message}"; isFileLoaded = false; Debug.LogError($"[EPPlus测试] 加载失败: {ex.Message}\n{ex.StackTrace}"); EditorUtility.DisplayDialog("加载失败", ex.Message, "确定"); } } /// /// 加载指定工作表的数据 /// 使用工作表名称来访问,避免索引问题(EPPlus的Worksheets集合索引可能不连续) /// private void LoadSheetData(int sheetIndex) { try { if (sheetIndex < 0 || sheetIndex >= sheetNames.Count) return; // 获取要加载的工作表名称 string sheetName = sheetNames[sheetIndex]; FileInfo fileInfo = new FileInfo(excelFilePath); using (ExcelPackage package = new ExcelPackage(fileInfo)) { // 使用名称访问工作表(更可靠,避免索引问题) ExcelWorksheet worksheet = package.Workbook.Worksheets[sheetName]; if (worksheet == null) { statusMessage = $"⚠️ 无法读取工作表: {sheetName}"; Debug.LogError($"[EPPlus测试] 工作表 '{sheetName}' 不存在"); return; } // 获取有效数据范围 if (worksheet.Dimension == null) { statusMessage = $"⚠️ 工作表 '{sheetName}' 为空"; sheetData = null; maxRows = 0; maxCols = 0; Debug.LogWarning($"[EPPlus测试] 工作表 '{sheetName}' 没有数据"); return; } maxRows = worksheet.Dimension.End.Row; maxCols = worksheet.Dimension.End.Column; // 创建二维数组存储数据 sheetData = new string[maxRows, maxCols]; // 读取所有单元格数据 // 注意:EPPlus 的索引从 1 开始,不是 0 for (int row = 1; row <= maxRows; row++) { for (int col = 1; col <= maxCols; col++) { var cell = worksheet.Cells[row, col]; sheetData[row - 1, col - 1] = cell.Value?.ToString() ?? ""; } } statusMessage = $"✅ 已加载工作表: {sheetName} ({maxRows}行 × {maxCols}列)"; // 重置滚动位置 dataScrollPosition = Vector2.zero; Debug.Log($"[EPPlus测试] 成功加载工作表: '{sheetName}'"); Debug.Log($"[EPPlus测试] 数据范围: {maxRows} 行 × {maxCols} 列"); // 打印前几行数据用于调试 if (maxRows > 0 && maxCols > 0) { Debug.Log($"[EPPlus测试] 第1行第1列: {sheetData[0, 0]}"); if (maxRows > 1) { Debug.Log($"[EPPlus测试] 第2行第1列: {sheetData[1, 0]}"); } } } } catch (Exception ex) { statusMessage = $"❌ 读取工作表失败: {ex.Message}"; Debug.LogError($"[EPPlus测试] 读取工作表失败: {ex.Message}\n{ex.StackTrace}"); } } } }