更新插件功能

This commit is contained in:
ShallowT1Dream
2026-05-29 16:57:16 +08:00
parent d06041c0de
commit 00eafcb5f9
3 changed files with 523 additions and 209 deletions

View File

@@ -12,6 +12,7 @@ public class ConfigTableInfo
public string description;
public List<FieldRelation> relations = new List<FieldRelation>();
public bool isExistInProject;
public string fileExtension = ".xlsx";
}
[Serializable]
@@ -24,13 +25,70 @@ public class FieldRelation
public string description;
}
public class ReverseRelation
{
public string sourceTable;
public string sourceDisplayName;
public string fieldName;
public string description;
public string relationFormat;
}
[Serializable]
public class FieldRelationBackup
{
public string fieldName;
public string targetTable;
public string targetField;
public string relationFormat;
public string description;
}
public static class ConfigLinkDatabase
{
private static List<ConfigTableInfo> _cachedData;
private static HashSet<string> _existingTableNames;
private static string _excelFolderPath;
private const string CONFIG_PATH = "Resources/Resources_moved/config";
public static void SetExcelFolderPath(string path)
{
_excelFolderPath = path;
PlayerPrefs.SetString("ConfigLinkExcelFolderPath", path);
}
public static string GetExcelFolderPath()
{
if (string.IsNullOrEmpty(_excelFolderPath))
{
_excelFolderPath = PlayerPrefs.GetString("ConfigLinkExcelFolderPath", "");
}
return _excelFolderPath;
}
public static string GetExcelFilePath(string tableName)
{
string folderPath = GetExcelFolderPath();
if (string.IsNullOrEmpty(folderPath))
return null;
var tableInfo = GetTableInfo(tableName);
string extension = tableInfo?.fileExtension ?? ".xlsx";
string excelPath = Path.Combine(folderPath, $"{tableName}{extension}");
return File.Exists(excelPath) ? excelPath : null;
}
public static bool IsExcelFolderSet()
{
return !string.IsNullOrEmpty(GetExcelFolderPath());
}
public static bool IsExcelFileExists(string tableName)
{
return GetExcelFilePath(tableName) != null;
}
public static List<ConfigTableInfo> GetAllTableInfo()
{
if (_cachedData != null)
@@ -319,7 +377,9 @@ public static class ConfigLinkDatabase
}},
new ConfigTableInfo { tableName = "fishingreward", displayName = "钓鱼奖励表", description = "钓鱼游戏奖励", relations = new List<FieldRelation> {
new FieldRelation { fieldName = "rewards", targetTable = "prop", targetField = "id", relationFormat = "type_id_num", description = "奖励" }
}}
}},
new ConfigTableInfo { tableName = "dirty_words", displayName = "敏感词表", description = "敏感词过滤配置TXT文件", fileExtension = ".txt" },
new ConfigTableInfo { tableName = "奖励格式说明", displayName = "奖励格式说明", description = "奖励数据格式说明文档TXT文件", fileExtension = ".txt" }
};
MarkExistingTables();
@@ -341,12 +401,62 @@ public static class ConfigLinkDatabase
}
}
string configPathAlt = Path.Combine(Application.dataPath, "../Resources/Resources_moved/config");
if (Directory.Exists(configPathAlt))
{
string[] jsonFilesAlt = Directory.GetFiles(configPathAlt, "*.json");
foreach (string file in jsonFilesAlt)
{
string tableName = Path.GetFileNameWithoutExtension(file);
_existingTableNames.Add(tableName.ToLower());
}
}
foreach (var table in _cachedData)
{
table.isExistInProject = _existingTableNames.Contains(table.tableName.ToLower());
bool existInConfig = _existingTableNames.Contains(table.tableName.ToLower());
if (!existInConfig && table.fileExtension == ".txt")
{
string excelFolderPath = GetExcelFolderPath();
if (!string.IsNullOrEmpty(excelFolderPath))
{
string txtPath = Path.Combine(excelFolderPath, $"{table.tableName}{table.fileExtension}");
existInConfig = File.Exists(txtPath);
}
}
table.isExistInProject = existInConfig;
}
}
public static bool IsTableExistInConfigFolder(string tableName)
{
string configPath = Path.Combine(Application.dataPath, CONFIG_PATH);
if (!Directory.Exists(configPath))
return false;
var tableInfo = GetTableInfo(tableName);
string extension = tableInfo?.fileExtension ?? ".xlsx";
string jsonPath = Path.Combine(configPath, $"{tableName}.json");
string configPathAlt = Path.Combine(Application.dataPath, "../Resources/Resources_moved/config");
string jsonPathAlt = Path.Combine(configPathAlt, $"{tableName}.json");
if (extension == ".txt")
{
string excelFolderPath = GetExcelFolderPath();
if (!string.IsNullOrEmpty(excelFolderPath))
{
string txtPath = Path.Combine(excelFolderPath, $"{tableName}{extension}");
if (File.Exists(txtPath))
return true;
}
}
return File.Exists(jsonPath) || File.Exists(jsonPathAlt);
}
public static ConfigTableInfo GetTableInfo(string tableName)
{
return GetAllTableInfo().Find(x => x.tableName.Equals(tableName, StringComparison.OrdinalIgnoreCase));
@@ -357,6 +467,90 @@ public static class ConfigLinkDatabase
return GetAllTableInfo().ConvertAll(x => x.tableName);
}
public static List<ReverseRelation> GetReverseRelations(string targetTableName)
{
List<ReverseRelation> result = new List<ReverseRelation>();
foreach (var table in GetAllTableInfo())
{
foreach (var relation in table.relations)
{
if (relation.targetTable.Equals(targetTableName, StringComparison.OrdinalIgnoreCase))
{
result.Add(new ReverseRelation
{
sourceTable = table.tableName,
sourceDisplayName = table.displayName,
fieldName = relation.fieldName,
description = relation.description,
relationFormat = relation.relationFormat
});
}
}
}
return result;
}
public static string FormatRelationValue(string format, string value)
{
if (string.IsNullOrEmpty(format) || string.IsNullOrEmpty(value))
return value;
string[] parts = value.Split('_');
string result = "";
switch (format.ToLower())
{
case "item_id_num":
if (parts.Length >= 3)
result = $"类型:{parts[0]}, ID:{parts[1]}, 数量:{parts[2]}";
break;
case "type_id_num":
if (parts.Length >= 3)
result = $"类型:{parts[0]}, ID:{parts[1]}, 数量:{parts[2]}";
break;
case "id_pos_lv":
if (parts.Length >= 3)
result = $"ID:{parts[0]}, 位置:{parts[1]}, 等级:{parts[2]}";
break;
case "id_lv_num":
if (parts.Length >= 3)
result = $"ID:{parts[0]}, 等级:{parts[1]}, 数量:{parts[2]}";
break;
case "id_lv_num_time":
if (parts.Length >= 4)
result = $"ID:{parts[0]}, 等级:{parts[1]}, 数量:{parts[2]}, 时间:{parts[3]}";
break;
case "buffid_lv":
if (parts.Length >= 2)
result = $"BuffID:{parts[0]}, 等级:{parts[1]}";
break;
case "rune_id_num":
if (parts.Length >= 2)
result = $"符文ID:{parts[0]}, 数量:{parts[1]}";
break;
case "equip_id_num":
if (parts.Length >= 2)
result = $"装备ID:{parts[0]}, 数量:{parts[1]}";
break;
case "表名":
result = $"表名: {value}";
break;
}
return string.IsNullOrEmpty(result) ? value : result;
}
public static List<string> GetRelatedTableNames(string tableName)
{
var tableInfo = GetTableInfo(tableName);
if (tableInfo == null)
return new List<string>();
return tableInfo.relations.Select(r => r.targetTable).Distinct().ToList();
}
public static List<string> GetAllDisplayNames()
{
return GetAllTableInfo().ConvertAll(x => $"{x.displayName} ({x.tableName})");

View File

@@ -2,33 +2,33 @@ using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System.IO;
public class ConfigLinkViewerWindow : EditorWindow
{
private string selectedTableName;
private Vector2 scrollPos;
private Dictionary<string, bool> expandedRelations = new Dictionary<string, bool>();
private Vector2 relationScrollPos;
private string searchFilter = "";
private bool showOnlyExisting = true;
private bool showReverseRelations = false;
[MenuItem("Tools/配置表联动查看器")]
public static void ShowWindow()
{
GetWindow<ConfigLinkViewerWindow>("配置表联动");
var window = GetWindow<ConfigLinkViewerWindow>("配置表联动");
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();
EditorGUILayout.Space();
if (!string.IsNullOrEmpty(selectedTableName))
{
DrawTableInfo();
}
DrawTableSelector(windowWidth, windowHeight);
}
private void DrawHeader()
@@ -39,7 +39,7 @@ public class ConfigLinkViewerWindow : EditorWindow
EditorGUILayout.EndVertical();
}
private void DrawTableSelector()
private void DrawTableSelector(float windowWidth, float windowHeight)
{
EditorGUILayout.BeginVertical("box");
@@ -47,9 +47,12 @@ public class ConfigLinkViewerWindow : EditorWindow
EditorGUILayout.BeginHorizontal();
searchFilter = EditorGUILayout.TextField("搜索:", searchFilter, EditorStyles.toolbarSearchField);
showOnlyExisting = EditorGUILayout.ToggleLeft("只显示当前项目存在的表", showOnlyExisting, GUILayout.Width(160));
showOnlyExisting = EditorGUILayout.ToggleLeft("只显示当前项目存在的表", showOnlyExisting);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
DrawExcelPathSettings(windowWidth);
var allTables = showOnlyExisting ? ConfigLinkDatabase.GetExistingTables() : ConfigLinkDatabase.GetAllTableInfo();
var filteredTables = string.IsNullOrEmpty(searchFilter)
? allTables
@@ -57,28 +60,32 @@ public class ConfigLinkViewerWindow : EditorWindow
t.tableName.Contains(searchFilter) ||
t.description.Contains(searchFilter)).ToList();
var displayNames = filteredTables.Select(x =>
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();
int selectedIndex = selectedTableName != null ? System.Array.IndexOf(tableKeys, selectedTableName) : 0;
selectedIndex = Mathf.Clamp(selectedIndex, 0, Mathf.Max(0, displayNames.Length - 1));
var tableKeys = filteredTables.Select(x => x.tableName).ToArray();
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical(GUILayout.Width(200));
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, GUILayout.Height(300));
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" : "Label";
GUIStyle buttonStyle = isSelected ? "Button" : listButtonStyle;
if (GUILayout.Button(displayNames[i], buttonStyle))
{
@@ -90,19 +97,22 @@ public class ConfigLinkViewerWindow : EditorWindow
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical("box", GUILayout.MinWidth(400));
DrawCurrentTableInfo();
EditorGUILayout.BeginVertical("box", GUILayout.Width(rightPanelWidth), GUILayout.Height(panelHeight));
DrawCurrentTableInfo(rightPanelWidth);
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
private void DrawCurrentTableInfo()
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;
}
@@ -110,6 +120,7 @@ public class ConfigLinkViewerWindow : EditorWindow
if (tableInfo == null)
{
EditorGUILayout.LabelField("未找到该表的配置信息", EditorStyles.helpBox);
EditorGUILayout.EndScrollView();
return;
}
@@ -121,25 +132,143 @@ public class ConfigLinkViewerWindow : EditorWindow
EditorGUILayout.Space();
if (tableInfo.relations.Count == 0)
DrawOpenExcelButtons(panelWidth);
EditorGUILayout.Space();
showReverseRelations = EditorGUILayout.ToggleLeft("显示反向引用", showReverseRelations);
EditorGUILayout.Space();
if (showReverseRelations)
{
EditorGUILayout.LabelField("该表暂无联动关系", EditorStyles.miniLabel);
DrawReverseRelations(panelWidth);
}
else
{
EditorGUILayout.LabelField($"联动关系 ({tableInfo.relations.Count}):", EditorStyles.boldLabel);
scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Height(250));
foreach (var relation in tableInfo.relations)
if (tableInfo.relations.Count == 0)
{
DrawRelation(relation);
EditorGUILayout.LabelField("该表暂无联动关系", EditorStyles.miniLabel);
}
else
{
EditorGUILayout.LabelField($"联动关系 ({tableInfo.relations.Count}):", EditorStyles.boldLabel);
EditorGUILayout.EndScrollView();
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 DrawRelation(FieldRelation relation)
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))
@@ -150,29 +279,33 @@ public class ConfigLinkViewerWindow : EditorWindow
bool targetExist = ConfigLinkDatabase.IsTableExist(relation.targetTable);
string existMark = targetExist ? "" : "[目标表缺失]";
EditorGUILayout.BeginVertical("frameBox");
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);
true, foldoutStyle);
if (expandedRelations[key])
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
EditorGUILayout.BeginVertical("helpBox");
EditorGUILayout.BeginVertical("helpBox", GUILayout.Width(panelWidth - 30));
EditorGUILayout.LabelField($"目标表: {relation.targetTable} {(targetExist ? "" : "()")}",
targetExist ? EditorStyles.miniLabel : EditorStyles.helpBox);
EditorGUILayout.LabelField($"目标字段: {relation.targetField}", EditorStyles.miniLabel);
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}", EditorStyles.miniLabel);
EditorGUILayout.LabelField($"格式: {relation.relationFormat}", labelStyle);
DrawFormatExample(relation.relationFormat);
}
EditorGUILayout.LabelField($"说明: {relation.description}", EditorStyles.miniLabel);
EditorGUILayout.LabelField($"说明: {relation.description}", labelStyle);
if (targetExist && GUILayout.Button($"查看 {relation.targetTable} 表", EditorStyles.miniButton, GUILayout.Width(120)))
if (targetExist && GUILayout.Button($"查看 {relation.targetTable} 表", EditorStyles.miniButton))
{
selectedTableName = relation.targetTable;
expandedRelations.Clear();
@@ -186,76 +319,48 @@ public class ConfigLinkViewerWindow : EditorWindow
EditorGUILayout.Space(2);
}
private void DrawTableInfo()
private void DrawFormatExample(string format)
{
var tableInfo = ConfigLinkDatabase.GetTableInfo(selectedTableName);
if (tableInfo == null) return;
string example = "";
string formatted = "";
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField($"当前表: {tableInfo.displayName} ({tableInfo.tableName})", EditorStyles.boldLabel);
EditorGUILayout.LabelField($"描述: {tableInfo.description}", EditorStyles.miniLabel);
EditorGUILayout.Space();
if (tableInfo.relations.Count == 0)
switch (format.ToLower())
{
EditorGUILayout.LabelField("暂无联动关系", EditorStyles.miniLabel);
}
else
{
EditorGUILayout.LabelField("联动关系:", EditorStyles.boldLabel);
scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Height(300));
foreach (var relation in tableInfo.relations)
{
DrawRelationItem(relation);
}
EditorGUILayout.EndScrollView();
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;
}
EditorGUILayout.EndVertical();
}
private void DrawRelationItem(FieldRelation relation)
{
string key = $"{selectedTableName}_{relation.fieldName}";
if (!expandedRelations.ContainsKey(key))
if (!string.IsNullOrEmpty(example))
{
expandedRelations[key] = true;
}
bool targetExist = ConfigLinkDatabase.IsTableExist(relation.targetTable);
expandedRelations[key] = EditorGUILayout.Foldout(expandedRelations[key],
$"字段: {relation.fieldName} → {relation.targetTable} {(targetExist ? "" : "()")}");
if (expandedRelations[key])
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField($"目标字段: {relation.targetField}", EditorStyles.miniLabel);
if (!string.IsNullOrEmpty(relation.relationFormat))
{
EditorGUILayout.LabelField($"数据格式: {relation.relationFormat}", EditorStyles.miniLabel);
}
EditorGUILayout.LabelField($"说明: {relation.description}", EditorStyles.miniLabel);
if (targetExist && GUILayout.Button($"跳转到 {relation.targetTable}", EditorStyles.miniButton))
{
selectedTableName = relation.targetTable;
Repaint();
return;
}
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false, richText = true };
EditorGUILayout.LabelField($"示例: {example} → {formatted}", labelStyle);
}
}
}