修改配置的插件
This commit is contained in:
@@ -5,14 +5,95 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
|
||||
public static class ConfigLinkViewerCallbacks
|
||||
{
|
||||
public static Action ExportConfigCallback;
|
||||
public static Func<string> FallbackExcelPathCallback;
|
||||
}
|
||||
|
||||
[InitializeOnLoad]
|
||||
public static class ConfigLinkViewerAutoSetup
|
||||
{
|
||||
static ConfigLinkViewerAutoSetup()
|
||||
{
|
||||
TryRegisterExportConfig();
|
||||
TryRegisterFallbackExcelPath();
|
||||
}
|
||||
|
||||
private static void TryRegisterExportConfig()
|
||||
{
|
||||
if (ConfigLinkViewerCallbacks.ExportConfigCallback != null) return;
|
||||
try
|
||||
{
|
||||
var type = FindType("MFrame.ConfigDeal");
|
||||
if (type == null) return;
|
||||
var method = type.GetMethod("ExportConfig",
|
||||
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||
if (method == null) return;
|
||||
ConfigLinkViewerCallbacks.ExportConfigCallback =
|
||||
(Action)Delegate.CreateDelegate(typeof(Action), method);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static void TryRegisterFallbackExcelPath()
|
||||
{
|
||||
if (ConfigLinkViewerCallbacks.FallbackExcelPathCallback != null) return;
|
||||
try
|
||||
{
|
||||
var pathConfType = FindType("MFrame.PathConf");
|
||||
var editorCfType = FindType("MFrame.ConfigEditorCf");
|
||||
if (pathConfType == null || editorCfType == null) return;
|
||||
|
||||
var rootField = pathConfType.GetField("project_root",
|
||||
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||
if (rootField == null) return;
|
||||
|
||||
var insProp = editorCfType.GetProperty("ins",
|
||||
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||
if (insProp == null) return;
|
||||
|
||||
var ins = insProp.GetValue(null);
|
||||
if (ins == null) return;
|
||||
|
||||
var excelField = ins.GetType().GetField("excel_path",
|
||||
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
|
||||
if (excelField == null) return;
|
||||
|
||||
ConfigLinkViewerCallbacks.FallbackExcelPathCallback = () =>
|
||||
{
|
||||
var root = rootField.GetValue(null) as string;
|
||||
var inst = insProp.GetValue(null);
|
||||
var ep = inst != null ? excelField.GetValue(inst) as string : null;
|
||||
return root + ep;
|
||||
};
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static Type FindType(string fullName)
|
||||
{
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
var t = asm.GetType(fullName);
|
||||
if (t != null) return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfigLinkViewerWindow : EditorWindow
|
||||
{
|
||||
private enum Tab { View, Config }
|
||||
|
||||
private Tab currentTab = Tab.View;
|
||||
private string selectedTableName;
|
||||
private Dictionary<string, bool> expandedRelations = new Dictionary<string, bool>();
|
||||
private Vector2 relationScrollPos;
|
||||
private Vector2 detailScrollPos;
|
||||
private Vector2 configScrollPos;
|
||||
private string searchFilter = "";
|
||||
private bool showOnlyExisting = true;
|
||||
private bool showReverseRelations = false;
|
||||
@@ -27,46 +108,284 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
DrawTabBar();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
float windowWidth = position.width;
|
||||
float windowHeight = position.height;
|
||||
|
||||
DrawHeader();
|
||||
|
||||
if (currentTab == Tab.View)
|
||||
{
|
||||
DrawViewTab(windowWidth, windowHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawConfigTab(windowWidth, windowHeight);
|
||||
DrawLuoTianyiOverlay(windowWidth, windowHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTabBar()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal("Toolbar");
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUIStyle tabLeft, tabRight;
|
||||
if (currentTab == Tab.View)
|
||||
{
|
||||
tabLeft = new GUIStyle("ToolbarButton") { fontStyle = FontStyle.Bold };
|
||||
tabRight = new GUIStyle("ToolbarButton");
|
||||
}
|
||||
else
|
||||
{
|
||||
tabLeft = new GUIStyle("ToolbarButton");
|
||||
tabRight = new GUIStyle("ToolbarButton") { fontStyle = FontStyle.Bold };
|
||||
}
|
||||
|
||||
if (GUILayout.Button("查看", tabLeft, GUILayout.Width(60)))
|
||||
{
|
||||
currentTab = Tab.View;
|
||||
}
|
||||
if (GUILayout.Button("配置", tabRight, GUILayout.Width(60)))
|
||||
{
|
||||
currentTab = Tab.Config;
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DrawViewTab(float windowWidth, float windowHeight)
|
||||
{
|
||||
DrawViewHeader();
|
||||
EditorGUILayout.Space();
|
||||
DrawTableSelector(windowWidth, windowHeight);
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
private void DrawViewHeader()
|
||||
{
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
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;
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
if (ConfigLinkViewerCallbacks.ExportConfigCallback != null)
|
||||
{
|
||||
ConfigLinkViewerCallbacks.ExportConfigCallback();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("提示",
|
||||
"未注册导出配置回调。\n请在项目初始化代码中设置 ConfigLinkViewerCallbacks.ExportConfigCallback。",
|
||||
"确定");
|
||||
}
|
||||
};
|
||||
}
|
||||
if (GUILayout.Button("刷新", GUILayout.Height(22), GUILayout.Width(60)))
|
||||
{
|
||||
ConfigLinkDatabase.ClearCache();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.LabelField("选择配置表查看其与其他表的关联关系", EditorStyles.miniLabel);
|
||||
|
||||
string configStatus = ConfigLinkDatabase.HasConfigFile()
|
||||
? "<color=green>已加载 (ConfigLinkData.json)</color>"
|
||||
: "<color=yellow>未找到配置文件,请切换到[配置]标签页生成</color>";
|
||||
var statusStyle = new GUIStyle(EditorStyles.miniLabel) { richText = true };
|
||||
EditorGUILayout.LabelField($"配置状态: {configStatus}", statusStyle);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawConfigTab(float windowWidth, float windowHeight)
|
||||
{
|
||||
configScrollPos = EditorGUILayout.BeginScrollView(configScrollPos);
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
|
||||
EditorGUILayout.LabelField("项目配置", new GUIStyle(EditorStyles.boldLabel) { fontSize = 14 });
|
||||
EditorGUILayout.Space();
|
||||
|
||||
DrawProjectSettings();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
DrawExcelPathSettings(windowWidth);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
EditorGUILayout.LabelField("配置数据生成", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField("首次使用或跨项目时,需要先生成配置数据。", EditorStyles.miniLabel);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("生成配置", GUILayout.Height(30)))
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
ConfigLinkDatabase.GenerateConfigFromExcelFolder();
|
||||
};
|
||||
}
|
||||
if (GUILayout.Button("补全配置", GUILayout.Height(30)))
|
||||
{
|
||||
EditorApplication.delayCall += RunAIConfigScript;
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("生成配置:从 Excel 文件夹扫描生成 ConfigLinkData.json 骨架", EditorStyles.miniLabel);
|
||||
EditorGUILayout.LabelField("补全配置:运行 Python 脚本自动分析引用关系,补全字段关联", EditorStyles.miniLabel);
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private GUIStyle _artStyle;
|
||||
|
||||
private GUIStyle GetArtStyle(int fontSize)
|
||||
{
|
||||
if (_artStyle == null || _artStyle.fontSize != fontSize)
|
||||
{
|
||||
_artStyle = new GUIStyle(EditorStyles.label)
|
||||
{
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
fontSize = fontSize,
|
||||
richText = true,
|
||||
wordWrap = false,
|
||||
clipping = TextClipping.Overflow,
|
||||
normal = { textColor = Color.white },
|
||||
};
|
||||
var font = Font.CreateDynamicFontFromOSFont("Consolas", fontSize);
|
||||
if (font != null)
|
||||
_artStyle.font = font;
|
||||
}
|
||||
return _artStyle;
|
||||
}
|
||||
|
||||
private void DrawLuoTianyiOverlay(float windowWidth, float windowHeight)
|
||||
{
|
||||
float scale = Mathf.Clamp(Mathf.Min(windowWidth, windowHeight) / 600f, 0.35f, 0.7f);
|
||||
int fontSize = Mathf.RoundToInt(9 * scale);
|
||||
float lineHeight = fontSize * 1.1f;
|
||||
|
||||
string[] lines = {
|
||||
" _________________",
|
||||
" ____/:::::::::::::::::\\_____",
|
||||
" __/::::::::::::::::::::::::::::\\___",
|
||||
" _/:::::::::::::::::::::::::::::::::::\\__",
|
||||
" _/::::::::::::::::::::::::::::::::::::::::\\_",
|
||||
" /::::::::::::::::::::::::::::::::::::::::::::\\",
|
||||
" |::::::::::::::::::::::::::::::::::::::::::::::\\",
|
||||
" /::::::::::::::::::::::::::::::::::::::::::::::::\\",
|
||||
" |:::/.:::::::;:::::::::::::::::::::::::::::::::::::|",
|
||||
" /:::/.:::::::/..:::::::::::::::::::::::::::::::::::::\\",
|
||||
" |:::|.::::::;/.::::::::::::::::::::::::::::::::::::::::|",
|
||||
" |::/.::::::/..:::::::;;'.::::::::::::::::::::::::::::::|",
|
||||
" |:|.::::/./.::::::;;/..:::::::::::::::::::::::::::::::::|",
|
||||
" `:|.:::|.|.:::::;/..;;;;;;-'.:;;;-':::::::::::::::::::::|",
|
||||
" \\|.:::|.|.:::;/.;;/ -..::'''...:::::::::::::::::::::::|",
|
||||
" \\;;::|.|.::/.;/--__ |::::::::::::::::::::::::::::|",
|
||||
" \\;;\\\\::/|/ =-__ --_ /::::::::::::::::::::::::::::::",
|
||||
" \\/ /| -._ |.::::::::::::::::::::::::::::::",
|
||||
" _.' /// /- ||::::::::::::::::::::::::::::::",
|
||||
" _.-' //' ||::::::::::::::::::::::::::::::",
|
||||
" | - `|::::::::::::::::::::::::::::::",
|
||||
" \\ \\:::::::::::::::::::::::::::::",
|
||||
" | \\:::::::::::::::::::::",
|
||||
" / __/:::::::::::::::::::::::::::",
|
||||
" \\ __/::;::;;:::::: ::::::::::",
|
||||
" |` /;;;;/::| \\:::: :___: :::::::::",
|
||||
" \\ |'_,::::/ \\ |:::: .| |`. :::::::::",
|
||||
" / _/::::::/ / /:::: | \\.' | ::. .::::",
|
||||
" | /.::;;:-'_)/_/::::: `._| .' ::..::::",
|
||||
" ----.__ | |.::| \\___/::::::: ::..::::",
|
||||
" :::::::`----\\_____ \\:::\\.-'::::::::::: ___:___ ::..::::",
|
||||
" ;;;;;:::::::::::::`------ \\:::::::::::::::: ___|___ ::..::::",
|
||||
" `-------:::::::\\ /:::::::::::::::: __| ::..::::",
|
||||
" ___.--------'::::::::\\ |::::::::::::::::: | |-. ::..::::",
|
||||
" :::::;;;:--:::::::::::| /::::::::::::::::: --' ' ::..::::",
|
||||
" ----' _,-:.:::::::::::\\ |.::::::::::::::::: ::..::::",
|
||||
" __/.::::::::::::::::| |.::::::::::::::::: : : ::..::::",
|
||||
" __/.:::;;::::::::;/.:::| |.::::::::::::::::: | : ::. .::::",
|
||||
" /.::::;/ /.:::::;/ |.::::| \\_.::::::::::::::: | . | :::::::::",
|
||||
" :::::/ /.:::::/ /.:::::| \\__.:::::::::::: `.' ' :::::::::",
|
||||
" ::::| |.:::::/ /.:::::.| \\,:::::::::::: ::::::::::",
|
||||
" ::::| |.::::| |.:::::/| __/::::::::::::::::::::::::::::",
|
||||
" \\.:::\\ \\.:::| |.::::||| __.--:::::::::::::::::::::::::::::",
|
||||
" \\.:::\\_ \\.:::\\ \\.:::'/.:::::::::::::::::::::::::::::::::",
|
||||
" \\.::::\\ \\.:::\\ \\.::::::::::::::::::::::::::::::::::::::::::"
|
||||
};
|
||||
|
||||
float boxW = Mathf.Clamp(windowWidth * 0.45f, 300f, 500f);
|
||||
float boxH = lines.Length * lineHeight + 16;
|
||||
float boxX = windowWidth - boxW - 10;
|
||||
float boxY = windowHeight - boxH - 10;
|
||||
|
||||
var bgColor = new Color(0.12f, 0.12f, 0.16f, 0.9f);
|
||||
EditorGUI.DrawRect(new Rect(boxX, boxY, boxW, boxH), bgColor);
|
||||
|
||||
var borderColor = new Color(0f, 0.75f, 1f, 0.4f);
|
||||
EditorGUI.DrawRect(new Rect(boxX, boxY, boxW, 1), borderColor);
|
||||
EditorGUI.DrawRect(new Rect(boxX, boxY + boxH - 1, boxW, 1), borderColor);
|
||||
EditorGUI.DrawRect(new Rect(boxX, boxY, 1, boxH), borderColor);
|
||||
EditorGUI.DrawRect(new Rect(boxX + boxW - 1, boxY, 1, boxH), borderColor);
|
||||
|
||||
GUIStyle artStyle = new GUIStyle(EditorStyles.label)
|
||||
{
|
||||
alignment = TextAnchor.MiddleCenter,
|
||||
fontSize = fontSize,
|
||||
richText = false,
|
||||
wordWrap = false,
|
||||
clipping = TextClipping.Overflow,
|
||||
};
|
||||
artStyle.normal.textColor = new Color(0f, 1f, 1f);
|
||||
|
||||
float y = boxY + 8;
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var lineRect = new Rect(boxX + 4, y, boxW - 8, lineHeight);
|
||||
GUI.Label(lineRect, lines[i], artStyle);
|
||||
y += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawProjectSettings()
|
||||
{
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
EditorGUILayout.LabelField("路径设置", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
string configPath = ConfigLinkDatabase.GetConfigFolderPath();
|
||||
EditorGUILayout.LabelField("Config目录:", GUILayout.Width(80));
|
||||
EditorGUILayout.LabelField(configPath, EditorStyles.textField, GUILayout.ExpandWidth(true));
|
||||
if (GUILayout.Button("选择", GUILayout.Width(60)))
|
||||
{
|
||||
string selected = EditorUtility.OpenFolderPanel("选择配置导出目录", Application.dataPath, "");
|
||||
if (!string.IsNullOrEmpty(selected))
|
||||
{
|
||||
ConfigLinkDatabase.SetConfigFolderPath(selected);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawTableSelector(float windowWidth, float windowHeight)
|
||||
{
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
|
||||
EditorGUILayout.LabelField("选择配置表:", EditorStyles.boldLabel);
|
||||
EditorGUILayout.BeginVertical("box", GUILayout.Height(windowHeight - 60));
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
searchFilter = EditorGUILayout.TextField("搜索:", searchFilter, EditorStyles.toolbarSearchField);
|
||||
showOnlyExisting = EditorGUILayout.ToggleLeft("只显示当前项目存在的表", showOnlyExisting);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
DrawExcelPathSettings(windowWidth);
|
||||
|
||||
var allTables = showOnlyExisting ? ConfigLinkDatabase.GetExistingTables() : ConfigLinkDatabase.GetAllTableInfo();
|
||||
var filteredTables = string.IsNullOrEmpty(searchFilter)
|
||||
? allTables
|
||||
@@ -84,13 +403,12 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.BeginHorizontal(GUILayout.ExpandHeight(true));
|
||||
|
||||
float leftPanelWidth = Mathf.Clamp(windowWidth * 0.3f, 180f, 300f);
|
||||
float rightPanelWidth = windowWidth - leftPanelWidth - 20f;
|
||||
float panelHeight = windowHeight - 150f;
|
||||
|
||||
EditorGUILayout.BeginVertical(GUILayout.Width(leftPanelWidth), GUILayout.Height(panelHeight));
|
||||
EditorGUILayout.BeginVertical(GUILayout.Width(leftPanelWidth), GUILayout.ExpandHeight(true));
|
||||
EditorGUILayout.LabelField($"配置表列表 ({filteredTables.Count}):", EditorStyles.miniBoldLabel);
|
||||
relationScrollPos = EditorGUILayout.BeginScrollView(relationScrollPos, false, true, GUILayout.ExpandHeight(true));
|
||||
|
||||
@@ -103,16 +421,22 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
|
||||
if (GUILayout.Button(displayNames[i], buttonStyle))
|
||||
{
|
||||
selectedTableName = tableKeys[i];
|
||||
expandedRelations.Clear();
|
||||
if (selectedTableName != tableKeys[i])
|
||||
{
|
||||
selectedTableName = tableKeys[i];
|
||||
expandedRelations.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.BeginVertical("box", GUILayout.Width(rightPanelWidth), GUILayout.Height(panelHeight));
|
||||
EditorGUILayout.BeginVertical(GUILayout.Width(rightPanelWidth), GUILayout.ExpandHeight(true));
|
||||
EditorGUILayout.LabelField("当前表详情:", EditorStyles.miniBoldLabel);
|
||||
detailScrollPos = EditorGUILayout.BeginScrollView(detailScrollPos, false, true, GUILayout.ExpandHeight(true));
|
||||
DrawCurrentTableInfo(rightPanelWidth);
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
@@ -121,12 +445,9 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
|
||||
private void DrawCurrentTableInfo(float panelWidth)
|
||||
{
|
||||
EditorGUILayout.BeginScrollView(Vector2.zero, false, true, GUILayout.ExpandHeight(true));
|
||||
|
||||
if (string.IsNullOrEmpty(selectedTableName))
|
||||
{
|
||||
EditorGUILayout.LabelField("请从左侧选择一个配置表", EditorStyles.centeredGreyMiniLabel);
|
||||
EditorGUILayout.EndScrollView();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -134,14 +455,13 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
if (tableInfo == null)
|
||||
{
|
||||
EditorGUILayout.LabelField("未找到该表的配置信息", EditorStyles.helpBox);
|
||||
EditorGUILayout.EndScrollView();
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField($"当前表: {tableInfo.displayName}", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField($"表名: {tableInfo.tableName}", EditorStyles.miniLabel);
|
||||
EditorGUILayout.LabelField($"描述: {tableInfo.description}", EditorStyles.miniLabel);
|
||||
EditorGUILayout.LabelField($"状态: {(tableInfo.isExistInProject ? "存在于当前项目" : "当前项目不存在")}",
|
||||
EditorGUILayout.LabelField($"状态: {(tableInfo.isExistInProject ? "存在于当前项目" : "当前项目不存在")}",
|
||||
tableInfo.isExistInProject ? EditorStyles.miniLabel : EditorStyles.helpBox);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
@@ -174,14 +494,12 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void DrawOpenExcelButtons(float panelWidth)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
|
||||
string excelPath = ConfigLinkDatabase.GetExcelFilePath(selectedTableName);
|
||||
bool canOpen = !string.IsNullOrEmpty(excelPath);
|
||||
|
||||
@@ -238,10 +556,10 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
{
|
||||
bool sourceExist = ConfigLinkDatabase.IsTableExist(rel.sourceTable);
|
||||
EditorGUILayout.BeginVertical("frameBox", GUILayout.Width(panelWidth - 10));
|
||||
|
||||
|
||||
GUIStyle headerStyle = new GUIStyle(sourceExist ? EditorStyles.boldLabel : EditorStyles.helpBox) { wordWrap = false };
|
||||
EditorGUILayout.LabelField($"{rel.sourceDisplayName} ({rel.sourceTable}) → {rel.fieldName}", headerStyle);
|
||||
|
||||
|
||||
GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false };
|
||||
EditorGUILayout.LabelField($"说明: {rel.description}", labelStyle);
|
||||
|
||||
@@ -264,14 +582,17 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
|
||||
private void DrawExcelPathSettings(float windowWidth)
|
||||
{
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
EditorGUILayout.LabelField("Excel 文件夹", EditorStyles.boldLabel);
|
||||
|
||||
string currentPath = ConfigLinkDatabase.GetExcelFolderPath();
|
||||
string displayPath = string.IsNullOrEmpty(currentPath) ? "未设置" : currentPath;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Excel文件夹:", GUILayout.Width(70));
|
||||
EditorGUILayout.LabelField("路径:", GUILayout.Width(36));
|
||||
EditorGUILayout.LabelField(displayPath, EditorStyles.textField, GUILayout.ExpandWidth(true));
|
||||
|
||||
if (GUILayout.Button("选择文件夹", GUILayout.Width(80)))
|
||||
|
||||
if (GUILayout.Button("选择", GUILayout.Width(60)))
|
||||
{
|
||||
string selectedPath = EditorUtility.OpenFolderPanel("选择Excel文件夹", "", "");
|
||||
if (!string.IsNullOrEmpty(selectedPath))
|
||||
@@ -280,6 +601,7 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawRelation(FieldRelation relation, float panelWidth)
|
||||
@@ -333,21 +655,128 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
EditorGUILayout.Space(2);
|
||||
}
|
||||
|
||||
private void DrawFormatExample(string format)
|
||||
{
|
||||
string example = ConfigLinkDatabase.FormatRelationValue(format, "1_2_3_4");
|
||||
if (example != "1_2_3_4")
|
||||
{
|
||||
GUIStyle exampleStyle = new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic };
|
||||
EditorGUILayout.LabelField($"示例: {example}", exampleStyle);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunAIConfigScript()
|
||||
{
|
||||
string scriptPath = Path.Combine(Application.dataPath, "Editor/ConfigLinkViewer", "generate_config_link_data.py");
|
||||
if (!File.Exists(scriptPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("提示", "未找到脚本文件:\n" + scriptPath, "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
string pythonExe = FindPythonExecutable();
|
||||
if (string.IsNullOrEmpty(pythonExe))
|
||||
{
|
||||
EditorUtility.DisplayDialog("提示", "未找到 Python 环境,请确保已安装 Python 并添加到 PATH", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string configDir = ConfigLinkDatabase.GetConfigFolderPath();
|
||||
string tableInfoPath = Path.Combine(Application.dataPath, "Editor/ConfigLinkViewer", "table_info.json");
|
||||
string outputPath = Path.Combine(Application.dataPath, "Editor/ConfigLinkViewer", "ConfigLinkData.json");
|
||||
|
||||
string arguments = $"\"{scriptPath}\" --config-dir \"{Path.Combine(Application.dataPath, configDir)}\" --output \"{outputPath}\"";
|
||||
if (File.Exists(tableInfoPath))
|
||||
{
|
||||
arguments += $" --table-info \"{tableInfoPath}\"";
|
||||
}
|
||||
|
||||
var startInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = pythonExe,
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = Path.GetDirectoryName(scriptPath),
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true,
|
||||
StandardOutputEncoding = System.Text.Encoding.UTF8,
|
||||
StandardErrorEncoding = System.Text.Encoding.UTF8,
|
||||
};
|
||||
|
||||
using (var process = System.Diagnostics.Process.Start(startInfo))
|
||||
{
|
||||
string stdout = process.StandardOutput.ReadToEnd();
|
||||
string stderr = process.StandardError.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
if (process.ExitCode == 0)
|
||||
{
|
||||
Debug.Log("[ConfigLinkViewer] 补全配置完成:\n" + stdout);
|
||||
ConfigLinkDatabase.ClearCache();
|
||||
AssetDatabase.Refresh();
|
||||
EditorUtility.DisplayDialog("完成", "补全配置已完成\n\n" + stdout, "确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[ConfigLinkViewer] 补全配置失败:\n" + stderr);
|
||||
EditorUtility.DisplayDialog("失败", "脚本执行失败:\n" + stderr, "确定");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError("[ConfigLinkViewer] 运行脚本异常: " + ex.Message);
|
||||
EditorUtility.DisplayDialog("异常", "运行脚本时发生异常:\n" + ex.Message, "确定");
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindPythonExecutable()
|
||||
{
|
||||
string[] candidates = { "python", "python3", "py" };
|
||||
foreach (var candidate in candidates)
|
||||
{
|
||||
try
|
||||
{
|
||||
var startInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = candidate,
|
||||
Arguments = "--version",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
using (var process = System.Diagnostics.Process.Start(startInfo))
|
||||
{
|
||||
process.WaitForExit();
|
||||
if (process.ExitCode == 0)
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void CleanExcelEmptyRows()
|
||||
{
|
||||
string excelFolder = ConfigLinkDatabase.GetExcelFolderPath();
|
||||
if (string.IsNullOrEmpty(excelFolder))
|
||||
{
|
||||
var acf = MFrame.ConfigEditorCf.ins;
|
||||
if (acf != null)
|
||||
if (ConfigLinkViewerCallbacks.FallbackExcelPathCallback != null)
|
||||
{
|
||||
excelFolder = MFrame.PathConf.project_root + acf.excel_path;
|
||||
excelFolder = ConfigLinkViewerCallbacks.FallbackExcelPathCallback();
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(excelFolder) || !Directory.Exists(excelFolder))
|
||||
{
|
||||
EditorUtility.DisplayDialog("提示", "Excel文件夹路径未设置或不存在,请先设置Excel文件夹", "确定");
|
||||
EditorUtility.DisplayDialog("提示",
|
||||
"Excel文件夹路径未设置或不存在。\n请切换到[配置]标签页设置,或在 ConfigLinkViewerCallbacks.FallbackExcelPathCallback 中注册回退逻辑。",
|
||||
"确定");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -416,91 +845,92 @@ public class ConfigLinkViewerWindow : EditorWindow
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
List<string> sharedStrings = null;
|
||||
using (var archive = ZipFile.Open(filePath, ZipArchiveMode.Read))
|
||||
{
|
||||
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)
|
||||
var ssEntry = archive.GetEntry("xl/sharedStrings.xml");
|
||||
if (ssEntry != null)
|
||||
{
|
||||
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);
|
||||
using (var stream = ssEntry.Open())
|
||||
{
|
||||
var ssDoc = XDocument.Load(stream);
|
||||
sharedStrings = ssDoc.Root.Elements(ns + "si")
|
||||
.Select(si => si.Value)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rowsToRemove.Count == 0) return 0;
|
||||
var sheetData = doc.Root.Element(ns + "sheetData");
|
||||
if (sheetData == null) return 0;
|
||||
|
||||
foreach (var row in rowsToRemove)
|
||||
var rows = sheetData.Elements(ns + "row").ToList();
|
||||
if (rows.Count <= headerRowCount) return 0;
|
||||
|
||||
var emptyRows = new List<XElement>();
|
||||
|
||||
for (int i = headerRowCount; i < rows.Count; i++)
|
||||
{
|
||||
var row = rows[i];
|
||||
var cells = row.Elements(ns + "c").ToList();
|
||||
|
||||
if (cells.Count == 0)
|
||||
{
|
||||
emptyRows.Add(row);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool allEmpty = true;
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
var value = cell.Element(ns + "v");
|
||||
if (value != null && !string.IsNullOrWhiteSpace(value.Value))
|
||||
{
|
||||
allEmpty = false;
|
||||
break;
|
||||
}
|
||||
|
||||
var cellType = cell.Attribute("t")?.Value;
|
||||
if (cellType == "s" && value != null)
|
||||
{
|
||||
if (int.TryParse(value.Value, out int ssIdx) && sharedStrings != null && ssIdx < sharedStrings.Count)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(sharedStrings[ssIdx]))
|
||||
{
|
||||
allEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allEmpty)
|
||||
{
|
||||
emptyRows.Add(row);
|
||||
}
|
||||
}
|
||||
|
||||
if (emptyRows.Count == 0) return 0;
|
||||
|
||||
foreach (var row in emptyRows)
|
||||
{
|
||||
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())
|
||||
var sheetEntry = archive.GetEntry("xl/worksheets/sheet1.xml");
|
||||
if (sheetEntry != null)
|
||||
{
|
||||
doc.Save(stream);
|
||||
sheetEntry.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);
|
||||
}
|
||||
return emptyRows.Count;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user