MeowmentDesign/Assets/Editor/CustomTools/ThriftPipeline/EPPlusTestEditor.cs
2026-02-01 15:37:46 +08:00

422 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using OfficeOpenXml;
namespace ThriftPipelineTools
{
/// <summary>
/// Excel 查看器 - 用于学习和测试 EPPlus 的 Excel 读取功能
///
/// 功能:
/// 1. 选择 Excel 文件 (.xlsx)
/// 2. 加载并显示所有工作表 (Sheet)
/// 3. 点击工作表查看数据内容
/// 4. 实时预览 Excel 数据结构
///
/// 学习目的:
/// - 理解 EPPlus 的基本用法
/// - 了解 Excel 文件的结构Workbook、Worksheet、Cells
/// - 掌握单元格数据的读取方法
/// </summary>
public class EPPlusTestEditor : EditorWindow
{
// ========== 文件路径 ==========
private string excelFilePath = ""; // 选中的Excel文件路径
// ========== Excel 数据 ==========
private List<string> sheetNames = new List<string>(); // 所有工作表名称
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; // 最多显示列数
/// <summary>
/// Unity 菜单项:打开 Excel 查看器窗口
/// </summary>
[MenuItem("蹊径/Thrift/Excel 查看器")]
public static void ShowWindow()
{
var window = GetWindow<EPPlusTestEditor>("Excel 查看器");
window.minSize = new Vector2(800, 600);
window.Show();
}
/// <summary>
/// 绘制 Editor 窗口 UI
/// </summary>
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();
}
}
/// <summary>
/// 绘制文件选择区域
/// </summary>
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();
}
/// <summary>
/// 绘制主要内容区域(工作表列表 + 数据表格)
/// </summary>
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();
}
/// <summary>
/// 绘制数据表格
/// </summary>
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();
}
/// <summary>
/// 加载 Excel 文件,获取所有工作表名称
/// </summary>
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, "确定");
}
}
/// <summary>
/// 加载指定工作表的数据
/// 使用工作表名称来访问避免索引问题EPPlus的Worksheets集合索引可能不连续
/// </summary>
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}");
}
}
}
}