添加个清理、导出功能

This commit is contained in:
ShallowT1Dream
2026-05-29 19:44:56 +08:00
parent 00eafcb5f9
commit 9a0faaccd7
2 changed files with 205 additions and 60 deletions

View File

@@ -1,8 +1,12 @@
using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.IO.Compression;
using System.Text.RegularExpressions;
using System.Xml.Linq;
public class ConfigLinkViewerWindow : EditorWindow
{
@@ -34,7 +38,17 @@ public class ConfigLinkViewerWindow : EditorWindow
private void DrawHeader()
{
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("配置表联动关系查看器", new GUIStyle(EditorStyles.boldLabel) { fontSize = 14 });
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("配置表联动关系查看器", new GUIStyle(EditorStyles.boldLabel) { fontSize = 14 }, GUILayout.ExpandWidth(true));
if (GUILayout.Button("清理Excel空行", GUILayout.Height(22), GUILayout.Width(100)))
{
EditorApplication.delayCall += CleanExcelEmptyRows;
}
if (GUILayout.Button("导出配置", GUILayout.Height(22), GUILayout.Width(80)))
{
EditorApplication.delayCall += MFrame.ConfigDeal.ExportConfig;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.LabelField("选择配置表查看其与其他表的关联关系", EditorStyles.miniLabel);
EditorGUILayout.EndVertical();
}
@@ -319,48 +333,174 @@ public class ConfigLinkViewerWindow : EditorWindow
EditorGUILayout.Space(2);
}
private void DrawFormatExample(string format)
private static void CleanExcelEmptyRows()
{
string example = "";
string formatted = "";
switch (format.ToLower())
string excelFolder = ConfigLinkDatabase.GetExcelFolderPath();
if (string.IsNullOrEmpty(excelFolder))
{
case "item_id_num":
case "type_id_num":
example = "1001_10_5";
formatted = "类型:1001, ID:10, 数量:5";
break;
case "id_pos_lv":
example = "100_1_30";
formatted = "ID:100, 位置:1, 等级:30";
break;
case "id_lv_num":
example = "100_30_5";
formatted = "ID:100, 等级:30, 数量:5";
break;
case "id_lv_num_time":
example = "100_30_5_120";
formatted = "ID:100, 等级:30, 数量:5, 时间:120秒";
break;
case "buffid_lv":
example = "5_3";
formatted = "BuffID:5, 等级:3";
break;
case "rune_id_num":
example = "10_2";
formatted = "符文ID:10, 数量:2";
break;
case "equip_id_num":
example = "20_1";
formatted = "装备ID:20, 数量:1";
break;
var acf = MFrame.ConfigEditorCf.ins;
if (acf != null)
{
excelFolder = MFrame.PathConf.project_root + acf.excel_path;
}
}
if (!string.IsNullOrEmpty(example))
if (string.IsNullOrEmpty(excelFolder) || !Directory.Exists(excelFolder))
{
GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false, richText = true };
EditorGUILayout.LabelField($"示例: {example} → {formatted}", labelStyle);
EditorUtility.DisplayDialog("提示", "Excel文件夹路径未设置或不存在请先设置Excel文件夹", "确定");
return;
}
var files = Directory.GetFiles(excelFolder, "*.xlsx", SearchOption.AllDirectories)
.Where(f => !Path.GetFileName(f).StartsWith("~$"))
.ToArray();
if (files.Length == 0)
{
EditorUtility.DisplayDialog("提示", "未找到Excel文件", "确定");
return;
}
int totalCleaned = 0;
int cleanedFileCount = 0;
try
{
foreach (var file in files)
{
try
{
int removedCount = CleanSingleExcelFile(file);
if (removedCount > 0)
{
totalCleaned += removedCount;
cleanedFileCount++;
}
}
catch (Exception ex)
{
Debug.LogError($"[清理空行] 处理文件失败: {Path.GetFileName(file)}, 错误: {ex.Message}");
}
}
}
finally
{
AssetDatabase.Refresh();
}
if (cleanedFileCount > 0)
{
EditorUtility.DisplayDialog("清理完成",
$"扫描 {files.Length} 个文件\n清理了 {cleanedFileCount} 个文件中共 {totalCleaned} 行空数据", "确定");
}
else
{
EditorUtility.DisplayDialog("清理完成",
$"扫描 {files.Length} 个文件,未发现需要清理的空行", "确定");
}
}
private static int CleanSingleExcelFile(string filePath)
{
const int headerRowCount = 3;
XNamespace ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
XDocument doc;
using (var archive = ZipFile.Open(filePath, ZipArchiveMode.Read))
{
var sheetEntry = archive.GetEntry("xl/worksheets/sheet1.xml");
if (sheetEntry == null) return 0;
using (var stream = sheetEntry.Open())
{
doc = XDocument.Load(stream);
}
}
var sheetData = doc.Root?.Element(ns + "sheetData");
if (sheetData == null) return 0;
var rows = sheetData.Elements(ns + "row").ToList();
var rowsToRemove = new List<XElement>();
foreach (var row in rows)
{
if (!int.TryParse(row.Attribute("r")?.Value, out int rowNum)) continue;
if (rowNum <= headerRowCount) continue;
var cells = row.Elements(ns + "c").ToList();
if (cells.Count == 0)
{
rowsToRemove.Add(row);
continue;
}
var firstCell = cells[0];
var valueElement = firstCell.Element(ns + "v");
string val = valueElement?.Value?.Trim();
if (string.IsNullOrEmpty(val) || !int.TryParse(val, out _))
{
rowsToRemove.Add(row);
}
}
if (rowsToRemove.Count == 0) return 0;
foreach (var row in rowsToRemove)
{
row.Remove();
}
var remainingRows = sheetData.Elements(ns + "row").ToList();
int newRowIndex = 1;
foreach (var row in remainingRows)
{
row.SetAttributeValue("r", newRowIndex);
foreach (var cell in row.Elements(ns + "c"))
{
string cellRef = cell.Attribute("r")?.Value;
if (!string.IsNullOrEmpty(cellRef))
{
string colPart = Regex.Replace(cellRef, @"\d", "");
cell.SetAttributeValue("r", colPart + newRowIndex);
}
}
newRowIndex++;
}
using (var archive = ZipFile.Open(filePath, ZipArchiveMode.Update))
{
var entry = archive.GetEntry("xl/worksheets/sheet1.xml");
if (entry != null) entry.Delete();
var newEntry = archive.CreateEntry("xl/worksheets/sheet1.xml");
using (var stream = newEntry.Open())
{
doc.Save(stream);
}
}
Debug.Log($"[清理空行] {Path.GetFileName(filePath)}: 删除了 {rowsToRemove.Count} 行空数据");
return rowsToRemove.Count;
}
private static readonly Dictionary<string, (string example, string desc)> FormatExamples = new Dictionary<string, (string, string)>
{
{ "item_id_num", ("1001_10_5", "类型:1001, ID:10, 数量:5") },
{ "type_id_num", ("1001_10_5", "类型:1001, ID:10, 数量:5") },
{ "id_pos_lv", ("100_1_30", "ID:100, 位置:1, 等级:30") },
{ "id_lv_num", ("100_30_5", "ID:100, 等级:30, 数量:5") },
{ "id_lv_num_time",("100_30_5_120","ID:100, 等级:30, 数量:5, 时间:120秒") },
{ "buffid_lv", ("5_3", "BuffID:5, 等级:3") },
{ "rune_id_num", ("10_2", "符文ID:10, 数量:2") },
{ "equip_id_num", ("20_1", "装备ID:20, 数量:1") },
};
private void DrawFormatExample(string format)
{
if (FormatExamples.TryGetValue(format.ToLower(), out var info))
{
var style = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false, richText = true };
EditorGUILayout.LabelField($"示例: {info.example} → {info.desc}", style);
}
}
}