using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Linq; using System.IO; public class ConfigLinkViewerWindow : EditorWindow { private string selectedTableName; private Dictionary expandedRelations = new Dictionary(); private Vector2 relationScrollPos; private string searchFilter = ""; private bool showOnlyExisting = true; private bool showReverseRelations = false; [MenuItem("Tools/配置表联动查看器")] public static void ShowWindow() { var window = GetWindow("配置表联动"); window.minSize = new Vector2(900, 600); window.position = new Rect(100, 100, 900, 600); } private void OnGUI() { float windowWidth = position.width; float windowHeight = position.height; DrawHeader(); EditorGUILayout.Space(); DrawTableSelector(windowWidth, windowHeight); } private void DrawHeader() { EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("配置表联动关系查看器", new GUIStyle(EditorStyles.boldLabel) { fontSize = 14 }); EditorGUILayout.LabelField("选择配置表查看其与其他表的关联关系", EditorStyles.miniLabel); EditorGUILayout.EndVertical(); } private void DrawTableSelector(float windowWidth, float windowHeight) { EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("选择配置表:", EditorStyles.boldLabel); 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 : allTables.Where(t => t.displayName.Contains(searchFilter) || t.tableName.Contains(searchFilter) || t.description.Contains(searchFilter)).ToList(); var displayNames = filteredTables.Select(x => { string existMark = showOnlyExisting ? "" : (x.isExistInProject ? "[存在]" : "[缺失]"); return $"{x.displayName} ({x.tableName}) {existMark}"; }).ToArray(); var tableKeys = filteredTables.Select(x => x.tableName).ToArray(); EditorGUILayout.Space(); EditorGUILayout.BeginHorizontal(); 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.LabelField($"配置表列表 ({filteredTables.Count}):", EditorStyles.miniBoldLabel); relationScrollPos = EditorGUILayout.BeginScrollView(relationScrollPos, false, true, GUILayout.ExpandHeight(true)); GUIStyle listButtonStyle = new GUIStyle(EditorStyles.label) { wordWrap = false, stretchWidth = true }; for (int i = 0; i < displayNames.Length; i++) { bool isSelected = tableKeys[i] == selectedTableName; GUIStyle buttonStyle = isSelected ? "Button" : listButtonStyle; if (GUILayout.Button(displayNames[i], buttonStyle)) { selectedTableName = tableKeys[i]; expandedRelations.Clear(); } } EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical("box", GUILayout.Width(rightPanelWidth), GUILayout.Height(panelHeight)); DrawCurrentTableInfo(rightPanelWidth); EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } 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; } var tableInfo = ConfigLinkDatabase.GetTableInfo(selectedTableName); 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 ? "存在于当前项目" : "当前项目不存在")}", tableInfo.isExistInProject ? EditorStyles.miniLabel : EditorStyles.helpBox); EditorGUILayout.Space(); DrawOpenExcelButtons(panelWidth); EditorGUILayout.Space(); showReverseRelations = EditorGUILayout.ToggleLeft("显示反向引用", showReverseRelations); EditorGUILayout.Space(); if (showReverseRelations) { DrawReverseRelations(panelWidth); } else { if (tableInfo.relations.Count == 0) { EditorGUILayout.LabelField("该表暂无联动关系", EditorStyles.miniLabel); } else { EditorGUILayout.LabelField($"联动关系 ({tableInfo.relations.Count}):", EditorStyles.boldLabel); foreach (var relation in tableInfo.relations) { DrawRelation(relation, panelWidth); } } } EditorGUILayout.EndScrollView(); } private void DrawOpenExcelButtons(float panelWidth) { EditorGUILayout.BeginHorizontal(); string excelPath = ConfigLinkDatabase.GetExcelFilePath(selectedTableName); bool canOpen = !string.IsNullOrEmpty(excelPath); if (canOpen) { if (GUILayout.Button("打开表格", GUILayout.Height(30), GUILayout.Width(panelWidth / 2 - 5))) { OpenExcelFile(selectedTableName); } } else { EditorGUILayout.LabelField("未找到Excel文件", EditorStyles.helpBox, GUILayout.Height(30), GUILayout.Width(panelWidth / 2 - 5)); } if (GUILayout.Button("批量打开关联表", GUILayout.Height(30), GUILayout.Width(panelWidth / 2 - 5))) { OpenRelatedTables(selectedTableName); } EditorGUILayout.EndHorizontal(); } private void OpenExcelFile(string tableName) { string excelPath = ConfigLinkDatabase.GetExcelFilePath(tableName); if (!string.IsNullOrEmpty(excelPath) && File.Exists(excelPath)) { System.Diagnostics.Process.Start(excelPath); } } private void OpenRelatedTables(string tableName) { var relatedTables = ConfigLinkDatabase.GetRelatedTableNames(tableName); foreach (var relatedTable in relatedTables) { OpenExcelFile(relatedTable); } } private void DrawReverseRelations(float panelWidth) { var reverseRels = ConfigLinkDatabase.GetReverseRelations(selectedTableName); if (reverseRels.Count == 0) { EditorGUILayout.LabelField("暂无其他表引用该表", EditorStyles.miniLabel); } else { EditorGUILayout.LabelField($"反向引用 ({reverseRels.Count}):", EditorStyles.boldLabel); foreach (var rel in reverseRels) { 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); if (!string.IsNullOrEmpty(rel.relationFormat)) { EditorGUILayout.LabelField($"格式: {rel.relationFormat}", labelStyle); } if (sourceExist && GUILayout.Button($"查看 {rel.sourceTable} 表", EditorStyles.miniButton)) { selectedTableName = rel.sourceTable; expandedRelations.Clear(); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(2); } } } private void DrawExcelPathSettings(float windowWidth) { string currentPath = ConfigLinkDatabase.GetExcelFolderPath(); string displayPath = string.IsNullOrEmpty(currentPath) ? "未设置" : currentPath; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Excel文件夹:", GUILayout.Width(70)); EditorGUILayout.LabelField(displayPath, EditorStyles.textField, GUILayout.ExpandWidth(true)); if (GUILayout.Button("选择文件夹", GUILayout.Width(80))) { string selectedPath = EditorUtility.OpenFolderPanel("选择Excel文件夹", "", ""); if (!string.IsNullOrEmpty(selectedPath)) { ConfigLinkDatabase.SetExcelFolderPath(selectedPath); } } EditorGUILayout.EndHorizontal(); } private void DrawRelation(FieldRelation relation, float panelWidth) { string key = $"{selectedTableName}_{relation.fieldName}_{relation.targetTable}"; if (!expandedRelations.ContainsKey(key)) { expandedRelations[key] = false; } bool targetExist = ConfigLinkDatabase.IsTableExist(relation.targetTable); string existMark = targetExist ? "" : "[目标表缺失]"; EditorGUILayout.BeginVertical("frameBox", GUILayout.Width(panelWidth - 10)); GUIStyle foldoutStyle = new GUIStyle(EditorStyles.foldout) { wordWrap = false, richText = true }; expandedRelations[key] = EditorGUILayout.Foldout(expandedRelations[key], $"{relation.fieldName} → {relation.targetTable}.{relation.targetField} {existMark}", true, foldoutStyle); if (expandedRelations[key]) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginVertical("helpBox", GUILayout.Width(panelWidth - 30)); GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false, richText = true }; EditorGUILayout.LabelField($"目标表: {relation.targetTable} {(targetExist ? "" : "(当前项目不存在)")}", labelStyle); EditorGUILayout.LabelField($"目标字段: {relation.targetField}", labelStyle); if (!string.IsNullOrEmpty(relation.relationFormat)) { EditorGUILayout.LabelField($"格式: {relation.relationFormat}", labelStyle); DrawFormatExample(relation.relationFormat); } EditorGUILayout.LabelField($"说明: {relation.description}", labelStyle); if (targetExist && GUILayout.Button($"查看 {relation.targetTable} 表", EditorStyles.miniButton)) { selectedTableName = relation.targetTable; expandedRelations.Clear(); } EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(2); } private void DrawFormatExample(string format) { string example = ""; string formatted = ""; switch (format.ToLower()) { 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; } if (!string.IsNullOrEmpty(example)) { GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false, richText = true }; EditorGUILayout.LabelField($"示例: {example} → {formatted}", labelStyle); } } }