using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using OfficeOpenXml; using ArtResource; using Debug = UnityEngine.Debug; namespace DesignTools.Collections { /// /// 表情配置Editor工具 /// 读取Docs/config/Emoji.xlsx,关联Art_SO/Collections/EmojiResource.asset /// public class EmojiConfigEditor : EditorWindow { private const string EMOJI_SO_PATH = "Assets/Art_SubModule/Art_SO/Collections"; private const string EMOJI_SO_NAME = "EmojiResource"; private const string EMOJI_EXCEL_NAME = "Emoji.xlsx"; private const string LANGUAGE_EXCEL_NAME = "AllLanguage.xlsx"; private const string EMOJI_SHEET_NAME = "Emoji"; private const string LANGUAGE_SHEET_NAME = "client"; private const string DOCS_PATH_PREF_KEY = "EmojiConfigEditor_DocsPath"; private const string DESIGN_SUBMODULE_PATH = "Assets/Design_SubModule"; private string docsRootPath = ""; private List emojiDataList = new List(); private ArtTableSO emojiTableSO; private Dictionary> languageDict = new Dictionary>(); private Vector2 scrollPosition; private bool isDataLoaded = false; private string pendingTooltipText = ""; [MenuItem("策划工具/收藏品/表情")] public static void ShowWindow() { var window = GetWindow("表情配置"); window.minSize = new Vector2(1000, 600); window.Show(); } private void OnEnable() { // 读取上次保存的路径 if (EditorPrefs.HasKey(DOCS_PATH_PREF_KEY)) { docsRootPath = EditorPrefs.GetString(DOCS_PATH_PREF_KEY); } } private void OnGUI() { EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); EditorGUILayout.LabelField("表情配置工具", EditorStyles.boldLabel); GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(5); // Docs路径选择 EditorGUILayout.BeginVertical("box"); EditorGUILayout.LabelField("Docs项目根目录", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); docsRootPath = EditorGUILayout.TextField("路径", docsRootPath); if (GUILayout.Button("选择文件夹", GUILayout.Width(100))) { string selectedPath = EditorUtility.OpenFolderPanel("选择Docs项目根目录", "", ""); if (!string.IsNullOrEmpty(selectedPath)) { docsRootPath = selectedPath; EditorPrefs.SetString(DOCS_PATH_PREF_KEY, docsRootPath); } } EditorGUILayout.EndHorizontal(); if (GUILayout.Button("加载配置数据", GUILayout.Height(30))) { LoadData(); } EditorGUILayout.EndVertical(); EditorGUILayout.Space(5); // 数据编辑区域 if (isDataLoaded) { DrawDataEditor(); } else { EditorGUILayout.HelpBox("请先选择Docs根目录并加载配置数据", MessageType.Info); } } /// /// 加载配置数据 /// private void LoadData() { try { // 校验路径 if (string.IsNullOrEmpty(docsRootPath) || !Directory.Exists(docsRootPath)) { EditorUtility.DisplayDialog("错误", "请选择有效的Docs根目录", "确定"); return; } // 1. 检查Docs是否为Git仓库并更新 if (!CheckAndUpdateDocsRepository()) { return; } // 2. 检查Design_SubModule分支 if (!CheckAndSwitchDesignSubModuleBranch()) { return; } // 校验Emoji SO是否存在 string emojiSOPath = Path.Combine(EMOJI_SO_PATH, $"{EMOJI_SO_NAME}.asset"); emojiTableSO = AssetDatabase.LoadAssetAtPath(emojiSOPath); if (emojiTableSO == null) { EditorUtility.DisplayDialog("错误", $"未找到表情资源配置\n路径: {emojiSOPath}\n\n请先在美术资源配置工具中创建", "确定"); return; } if (emojiTableSO.Items == null || emojiTableSO.Items.Count == 0) { EditorUtility.DisplayDialog("错误", "表情资源配置数据为空", "确定"); return; } // 加载语言表 LoadLanguageData(); // 加载Emoji.xlsx LoadEmojiExcel(); isDataLoaded = true; EditorUtility.DisplayDialog("成功", "配置数据加载成功!", "确定"); } catch (Exception e) { EditorUtility.DisplayDialog("错误", $"加载数据失败: {e.Message}\n{e.StackTrace}", "确定"); isDataLoaded = false; } } /// /// 执行Git命令 /// private string ExecuteGitCommand(string workingDirectory, string arguments, out bool success) { try { ProcessStartInfo startInfo = new ProcessStartInfo { FileName = "git", Arguments = arguments, WorkingDirectory = workingDirectory, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; using (Process process = Process.Start(startInfo)) { string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); success = process.ExitCode == 0; return success ? output : error; } } catch (Exception e) { success = false; return $"执行Git命令失败: {e.Message}"; } } /// /// 检查并更新Docs仓库 /// private bool CheckAndUpdateDocsRepository() { string gitPath = Path.Combine(docsRootPath, ".git"); if (!Directory.Exists(gitPath)) { EditorUtility.DisplayDialog("错误", $"Docs目录不是Git仓库\n路径: {docsRootPath}\n\n请确保Docs项目已正确克隆", "确定"); return false; } EditorUtility.DisplayProgressBar("检查更新", "正在检查Docs仓库远程更新...", 0.3f); string fetchResult = ExecuteGitCommand(docsRootPath, "fetch", out bool fetchSuccess); if (!fetchSuccess) { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("Git Fetch失败", $"无法检查远程更新:\n{fetchResult}\n\n请在SourceTree或GitHubDesktop中检查网络连接和仓库状态", "确定"); return false; } EditorUtility.DisplayProgressBar("检查更新", "正在检查是否有新提交...", 0.6f); string statusResult = ExecuteGitCommand(docsRootPath, "status -uno", out bool statusSuccess); if (!statusSuccess) { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("Git Status失败", $"无法获取仓库状态:\n{statusResult}\n\n请在SourceTree或GitHubDesktop中检查仓库状态", "确定"); return false; } if (statusResult.Contains("Changes not staged") || statusResult.Contains("Changes to be committed")) { EditorUtility.ClearProgressBar(); bool proceed = EditorUtility.DisplayDialog("警告:有未提交的更改", "Docs仓库中有未提交的更改,这可能导致拉取时产生冲突。\n\n建议先提交或暂存这些更改。\n\n是否继续加载配置?(不推荐)", "继续(不推荐)", "取消,前往处理"); if (!proceed) { Debug.Log("请在SourceTree或GitHubDesktop中处理未提交的更改"); return false; } } if (statusResult.Contains("Your branch is behind")) { EditorUtility.DisplayProgressBar("更新中", "正在从远程拉取最新代码...", 0.8f); string pullResult = ExecuteGitCommand(docsRootPath, "pull", out bool pullSuccess); EditorUtility.ClearProgressBar(); if (!pullSuccess) { if (pullResult.Contains("CONFLICT") || pullResult.Contains("conflict")) { EditorUtility.DisplayDialog("拉取失败:存在冲突", $"拉取远程更新时发生冲突:\n{pullResult}\n\n请在SourceTree或GitHubDesktop中解决冲突后再操作", "确定"); } else { EditorUtility.DisplayDialog("拉取失败", $"无法拉取远程更新:\n{pullResult}\n\n请在SourceTree或GitHubDesktop中检查并解决问题", "确定"); } return false; } Debug.Log($"Docs仓库已更新到最新版本:\n{pullResult}"); EditorUtility.DisplayDialog("更新成功", "Docs仓库已更新到最新版本", "确定"); } else { EditorUtility.ClearProgressBar(); Debug.Log("Docs仓库已是最新版本"); } return true; } /// /// 检查并切换Design_SubModule到main分支 /// private bool CheckAndSwitchDesignSubModuleBranch() { string designSubModulePath = Path.Combine(Application.dataPath, "..", DESIGN_SUBMODULE_PATH); designSubModulePath = Path.GetFullPath(designSubModulePath); if (!Directory.Exists(designSubModulePath)) { EditorUtility.DisplayDialog("错误", $"Design_SubModule目录不存在:\n{designSubModulePath}\n\n请确保子模块已正确初始化", "确定"); return false; } EditorUtility.DisplayProgressBar("检查分支", "正在检查Design_SubModule分支...", 0.5f); string branchResult = ExecuteGitCommand(designSubModulePath, "branch --show-current", out bool branchSuccess); if (!branchSuccess) { EditorUtility.ClearProgressBar(); EditorUtility.DisplayDialog("Git Branch失败", $"无法获取当前分支:\n{branchResult}\n\n请在SourceTree或GitHubDesktop中检查Design_SubModule状态", "确定"); return false; } string currentBranch = branchResult.Trim(); if (currentBranch != "main") { EditorUtility.DisplayProgressBar("切换分支", "正在切换到main分支...", 0.8f); string checkoutResult = ExecuteGitCommand(designSubModulePath, "checkout main", out bool checkoutSuccess); EditorUtility.ClearProgressBar(); if (!checkoutSuccess) { EditorUtility.DisplayDialog("切换分支失败", $"无法切换到main分支:\n{checkoutResult}\n\n当前分支: {currentBranch}\n\n请在SourceTree或GitHubDesktop中手动切换到main分支", "确定"); return false; } Debug.Log($"Design_SubModule已从 {currentBranch} 切换到 main 分支"); EditorUtility.DisplayDialog("分支切换成功", $"Design_SubModule已从 {currentBranch} 切换到 main 分支", "确定"); } else { EditorUtility.ClearProgressBar(); Debug.Log("Design_SubModule已在main分支"); } return true; } /// /// 加载语言数据 /// private void LoadLanguageData() { languageDict.Clear(); string languageExcelPath = Path.Combine(docsRootPath, "config", LANGUAGE_EXCEL_NAME); if (!File.Exists(languageExcelPath)) { Debug.LogWarning($"未找到语言表: {languageExcelPath}"); return; } using (var package = new ExcelPackage(new FileInfo(languageExcelPath))) { var worksheet = package.Workbook.Worksheets[LANGUAGE_SHEET_NAME]; if (worksheet == null) { Debug.LogWarning($"语言表中未找到Sheet: {LANGUAGE_SHEET_NAME}"); return; } // 查找Key列和语言列 int keyColumnIndex = -1; int zhCNColumnIndex = -1; int enUSColumnIndex = -1; int ptBRColumnIndex = -1; int columnCount = worksheet.Dimension.Columns; for (int col = 1; col <= columnCount; col++) { string header = worksheet.Cells[1, col].Text; if (header == "key") { keyColumnIndex = col; } else if (header == "zh_CN") { zhCNColumnIndex = col; } else if (header == "en_US") { enUSColumnIndex = col; } else if (header == "pt_BR") { ptBRColumnIndex = col; } } if (keyColumnIndex < 0) { Debug.LogWarning("语言表中未找到Key列"); return; } // 读取数据(从第3行开始) int rowCount = worksheet.Dimension.Rows; for (int row = 3; row <= rowCount; row++) { string key = worksheet.Cells[row, keyColumnIndex].Text; if (string.IsNullOrEmpty(key)) continue; if (!languageDict.ContainsKey(key)) { languageDict[key] = new Dictionary(); } if (zhCNColumnIndex > 0) { languageDict[key]["zh_CN"] = worksheet.Cells[row, zhCNColumnIndex].Text; } if (enUSColumnIndex > 0) { languageDict[key]["en_US"] = worksheet.Cells[row, enUSColumnIndex].Text; } if (ptBRColumnIndex > 0) { languageDict[key]["pt_BR"] = worksheet.Cells[row, ptBRColumnIndex].Text; } } } } /// /// 加载Emoji.xlsx /// private void LoadEmojiExcel() { emojiDataList.Clear(); string emojiExcelPath = Path.Combine(docsRootPath, "config", EMOJI_EXCEL_NAME); if (!File.Exists(emojiExcelPath)) { throw new Exception($"未找到Emoji配置文件: {emojiExcelPath}"); } using (var package = new ExcelPackage(new FileInfo(emojiExcelPath))) { var worksheet = package.Workbook.Worksheets[EMOJI_SHEET_NAME]; if (worksheet == null) { throw new Exception($"Emoji.xlsx中未找到Sheet: {EMOJI_SHEET_NAME}"); } // 读取表头(第1行) int idCol = -1, nameKeyCol = -1, initCol = -1, iconCol = -1; int columnCount = worksheet.Dimension.Columns; for (int col = 1; col <= columnCount; col++) { string header = worksheet.Cells[1, col].Text; switch (header) { case "Id": idCol = col; break; case "NameKey": nameKeyCol = col; break; case "Init": initCol = col; break; case "Icon": iconCol = col; break; } } if (idCol < 0 || nameKeyCol < 0 || initCol < 0 || iconCol < 0) { throw new Exception("Emoji.xlsx表结构不正确,缺少必要列(Id/NameKey/Init/Icon)"); } // 读取数据(从第3行开始) int rowCount = worksheet.Dimension.Rows; for (int row = 3; row <= rowCount; row++) { string idText = worksheet.Cells[row, idCol].Text; if (string.IsNullOrEmpty(idText)) continue; if (!int.TryParse(idText, out int id)) continue; string nameKey = worksheet.Cells[row, nameKeyCol].Text; string initText = worksheet.Cells[row, initCol].Text; string iconText = worksheet.Cells[row, iconCol].Text; int init = 0; if (!string.IsNullOrEmpty(initText)) { int.TryParse(initText, out init); } int iconId = -1; if (!string.IsNullOrEmpty(iconText)) { if (!int.TryParse(iconText, out iconId)) { iconId = -1; } } var emojiData = new EmojiData { Id = id, NameKey = nameKey, Init = init, IconId = iconId }; emojiDataList.Add(emojiData); } } } /// /// 绘制数据编辑器 /// private void DrawDataEditor() { pendingTooltipText = ""; EditorGUILayout.BeginVertical("box"); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField($"表情数据列表(共 {emojiDataList.Count} 条)", EditorStyles.boldLabel); GUILayout.FlexibleSpace(); GUI.backgroundColor = Color.cyan; if (GUILayout.Button("+ 添加表情", GUILayout.Height(25), GUILayout.Width(100))) { AddNewEmoji(); } GUI.backgroundColor = Color.white; EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(3); scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); // 表头 EditorGUILayout.BeginHorizontal("box"); EditorGUILayout.LabelField("Id", EditorStyles.boldLabel, GUILayout.Width(50)); EditorGUILayout.LabelField("NameKey", EditorStyles.boldLabel, GUILayout.Width(150)); EditorGUILayout.LabelField("中文名称", EditorStyles.boldLabel, GUILayout.Width(120)); EditorGUILayout.LabelField("Init", EditorStyles.boldLabel, GUILayout.Width(50)); EditorGUILayout.LabelField("Icon", EditorStyles.boldLabel, GUILayout.Width(200)); EditorGUILayout.LabelField("预览", EditorStyles.boldLabel, GUILayout.Width(100)); EditorGUILayout.LabelField("操作", EditorStyles.boldLabel, GUILayout.Width(60)); EditorGUILayout.EndHorizontal(); // 数据行 for (int i = 0; i < emojiDataList.Count; i++) { DrawEmojiDataRow(emojiDataList[i]); } EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); EditorGUILayout.Space(10); // 保存按钮 EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.backgroundColor = Color.green; if (GUILayout.Button("保存配置到Excel", GUILayout.Height(30), GUILayout.Width(200))) { SaveData(); } GUI.backgroundColor = Color.white; GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); // 在所有内容绘制完后,绘制tooltip(确保在最上层) if (!string.IsNullOrEmpty(pendingTooltipText)) { Vector2 tooltipSize = GUI.skin.box.CalcSize(new GUIContent(pendingTooltipText)); tooltipSize.x += 10; tooltipSize.y += 10; Vector2 mousePos = Event.current.mousePosition; Rect tooltipRect = new Rect( mousePos.x + 1, mousePos.y + 1, tooltipSize.x, tooltipSize.y ); GUI.Box(tooltipRect, pendingTooltipText); } } /// /// 绘制单行数据 /// private void DrawEmojiDataRow(EmojiData data) { EditorGUILayout.BeginHorizontal("box"); // Id(可编辑) data.Id = EditorGUILayout.IntField(data.Id, GUILayout.Width(50)); // NameKey(可编辑) data.NameKey = EditorGUILayout.TextField(data.NameKey, GUILayout.Width(150)); // 中文名称(只读预览,带即时多语言显示) GUI.enabled = false; string zhName = "未找到语言Key"; if (languageDict.ContainsKey(data.NameKey)) { var langs = languageDict[data.NameKey]; if (langs.ContainsKey("zh_CN")) { zhName = langs["zh_CN"]; } } Rect nameRect = GUILayoutUtility.GetRect(new GUIContent(zhName), GUI.skin.textField, GUILayout.Width(120)); EditorGUI.TextField(nameRect, zhName); // 检测鼠标悬停并准备tooltip内容 if (nameRect.Contains(Event.current.mousePosition) && languageDict.ContainsKey(data.NameKey)) { var langs = languageDict[data.NameKey]; List tooltipLines = new List(); if (langs.ContainsKey("zh_CN") && !string.IsNullOrEmpty(langs["zh_CN"])) { tooltipLines.Add($"[中文] {langs["zh_CN"]}"); } if (langs.ContainsKey("en_US") && !string.IsNullOrEmpty(langs["en_US"])) { tooltipLines.Add($"[English] {langs["en_US"]}"); } if (langs.ContainsKey("pt_BR") && !string.IsNullOrEmpty(langs["pt_BR"])) { tooltipLines.Add($"[Português] {langs["pt_BR"]}"); } if (tooltipLines.Count > 0) { pendingTooltipText = string.Join("\n", tooltipLines); Repaint(); } } GUI.enabled = true; // Init(Checkbox) bool initChecked = data.Init == 1; bool newInitChecked = EditorGUILayout.Toggle(initChecked, GUILayout.Width(50)); data.Init = newInitChecked ? 1 : 0; // Icon(下拉列表) var iconItems = emojiTableSO.Items; var iconNamesList = new List { "未选择" }; iconNamesList.AddRange(iconItems.Select(x => x.Name)); var iconNames = iconNamesList.ToArray(); int currentIndex = 0; var currentItem = iconItems.Find(x => x.Id == data.IconId); if (currentItem != null) { int itemIndex = iconItems.IndexOf(currentItem); if (itemIndex >= 0) { currentIndex = itemIndex + 1; } } EditorGUI.BeginChangeCheck(); int newIndex = EditorGUILayout.Popup(currentIndex, iconNames, GUILayout.Width(200)); if (EditorGUI.EndChangeCheck()) { if (newIndex == 0) { data.IconId = -1; } else if (newIndex > 0 && newIndex <= iconItems.Count) { data.IconId = iconItems[newIndex - 1].Id; } } // 预览 if (data.IconId >= 0) { var item = iconItems.Find(x => x.Id == data.IconId); if (item != null) { Sprite sprite = item.Sprite; if (sprite != null && sprite.texture != null) { Rect previewRect = GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50)); EditorGUI.DrawRect(previewRect, new Color(0.5f, 0.5f, 0.5f, 1f)); Rect texCoords = sprite.textureRect; Texture2D tex = sprite.texture; Rect normalizedCoords = new Rect( texCoords.x / tex.width, texCoords.y / tex.height, texCoords.width / tex.width, texCoords.height / tex.height ); float aspect = texCoords.width / texCoords.height; Rect drawRect = previewRect; if (aspect > 1f) { float height = drawRect.width / aspect; drawRect.y += (drawRect.height - height) * 0.5f; drawRect.height = height; } else { float width = drawRect.height * aspect; drawRect.x += (drawRect.width - width) * 0.5f; drawRect.width = width; } GUI.DrawTextureWithTexCoords(drawRect, tex, normalizedCoords, true); if (!string.IsNullOrEmpty(item.Desc)) { GUI.Label(previewRect, new GUIContent("", item.Desc)); } } else { GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50)); GUILayout.Space(-50); EditorGUILayout.LabelField("无图片", GUILayout.Width(50)); } } else { GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50)); GUILayout.Space(-50); EditorGUILayout.LabelField("未选择", GUILayout.Width(50)); } } else { GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50)); GUILayout.Space(-50); EditorGUILayout.LabelField("未选择", GUILayout.Width(50)); } // 删除按钮 GUI.backgroundColor = Color.red; if (GUILayout.Button("删除", GUILayout.Width(60))) { if (EditorUtility.DisplayDialog("确认删除", $"确定要删除表情 {data.Id} ({data.NameKey}) 吗?", "删除", "取消")) { emojiDataList.Remove(data); } } GUI.backgroundColor = Color.white; EditorGUILayout.EndHorizontal(); } /// /// 添加新表情 /// private void AddNewEmoji() { int newId = 1; if (emojiDataList.Count > 0) { newId = emojiDataList.Max(x => x.Id) + 1; } var newEmoji = new EmojiData { Id = newId, NameKey = "", Init = 0, IconId = -1 }; emojiDataList.Add(newEmoji); scrollPosition = new Vector2(0, float.MaxValue); } /// /// 保存数据到Excel /// private void SaveData() { try { string emojiExcelPath = Path.Combine(docsRootPath, "config", EMOJI_EXCEL_NAME); if (!File.Exists(emojiExcelPath)) { EditorUtility.DisplayDialog("错误", $"未找到Emoji配置文件: {emojiExcelPath}", "确定"); return; } using (var package = new ExcelPackage(new FileInfo(emojiExcelPath))) { var worksheet = package.Workbook.Worksheets[EMOJI_SHEET_NAME]; if (worksheet == null) { EditorUtility.DisplayDialog("错误", $"Emoji.xlsx中未找到Sheet: {EMOJI_SHEET_NAME}", "确定"); return; } // 查找列索引 int idCol = -1, nameKeyCol = -1, initCol = -1, iconCol = -1; int columnCount = worksheet.Dimension.Columns; for (int col = 1; col <= columnCount; col++) { string header = worksheet.Cells[1, col].Text; switch (header) { case "Id": idCol = col; break; case "NameKey": nameKeyCol = col; break; case "Init": initCol = col; break; case "Icon": iconCol = col; break; } } // 更新和删除数据(从第3行开始) int rowCount = worksheet.Dimension.Rows; var processedIds = new HashSet(); // 第一遍:更新现有行或删除 for (int row = rowCount; row >= 3; row--) { string idText = worksheet.Cells[row, idCol].Text; if (string.IsNullOrEmpty(idText)) continue; if (!int.TryParse(idText, out int id)) continue; var emojiData = emojiDataList.Find(x => x.Id == id); if (emojiData != null) { worksheet.Cells[row, nameKeyCol].Value = emojiData.NameKey; worksheet.Cells[row, initCol].Value = emojiData.Init; if (emojiData.IconId < 0) { worksheet.Cells[row, iconCol].Value = ""; } else { worksheet.Cells[row, iconCol].Value = emojiData.IconId; } processedIds.Add(id); } else { worksheet.DeleteRow(row); } } // 第二遍:添加新行 int currentRow = worksheet.Dimension?.Rows ?? 2; foreach (var emojiData in emojiDataList) { if (!processedIds.Contains(emojiData.Id)) { currentRow++; worksheet.Cells[currentRow, idCol].Value = emojiData.Id; worksheet.Cells[currentRow, nameKeyCol].Value = emojiData.NameKey; worksheet.Cells[currentRow, initCol].Value = emojiData.Init; if (emojiData.IconId < 0) { worksheet.Cells[currentRow, iconCol].Value = ""; } else { worksheet.Cells[currentRow, iconCol].Value = emojiData.IconId; } } } package.Save(); } // 提示成功并提醒推送 bool understood = EditorUtility.DisplayDialog("保存成功", "配置已保存到Excel文件!\n\n" + "⚠️ 重要提醒:\n" + "请及时在SourceTree或GitHubDesktop中:\n" + "1. 提交(Commit)本次修改\n" + "2. 推送(Push)到远程仓库\n\n" + "避免与其他策划产生冲突!", "我知道了"); } catch (Exception e) { EditorUtility.DisplayDialog("错误", $"保存数据失败: {e.Message}\n{e.StackTrace}", "确定"); } } /// /// 表情数据类 /// private class EmojiData { public int Id; public string NameKey; public int Init; public int IconId; } } }