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}");
}
}
}
}