using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using OfficeOpenXml;
using ArtResource;
namespace DesignTools
{
///
/// 一次性工具:将 Emoji.xlsx / Face.xlsx / Avatar.xlsx 的 Icon 字段从 ArtItemData.Id 迁移为 ArtItemData.Name
/// 使用完毕后可删除此文件
///
public static class MigrateIconFormatTool
{
private static readonly string[] SO_SEARCH_PATHS = new[]
{
"Assets/Art_SubModule/Art_SO",
"Assets/Art_SubModule/Art_SO/Collections"
};
private struct MigrationTarget
{
public string ExcelName;
public string SheetName;
public string SOTableName;
}
private static readonly MigrationTarget[] Targets = new[]
{
new MigrationTarget { ExcelName = "Emoji.xlsx", SheetName = "Emoji", SOTableName = "EmojiResource" },
new MigrationTarget { ExcelName = "Face.xlsx", SheetName = "Face", SOTableName = "HeadResource" },
new MigrationTarget { ExcelName = "Avatar.xlsx", SheetName = "Avatar", SOTableName = "HeadFrameResource" },
};
[MenuItem("策划工具/一次性迁移/Icon格式 ID→Name (Emoji+Face+Avatar)")]
public static void Execute()
{
// 1. 选择 Docs/config 目录
string configDir = EditorUtility.OpenFolderPanel("选择 Docs/config 目录(包含 Emoji.xlsx、Face.xlsx、Avatar.xlsx)", "", "");
if (string.IsNullOrEmpty(configDir)) return;
// 2. 加载所有 ArtTableSO
var allArtTables = new List();
foreach (string searchPath in SO_SEARCH_PATHS)
{
string[] guids = AssetDatabase.FindAssets("t:ArtTableSO", new[] { searchPath });
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
var so = AssetDatabase.LoadAssetAtPath(path);
if (so != null && !allArtTables.Any(x => x.TableName == so.TableName))
allArtTables.Add(so);
}
}
if (allArtTables.Count == 0)
{
EditorUtility.DisplayDialog("错误", "未找到任何 ArtTableSO 资源", "确定");
return;
}
int totalMigrated = 0;
int totalSkipped = 0;
int totalErrors = 0;
var allMessages = new List();
foreach (var target in Targets)
{
string excelPath = Path.Combine(configDir, target.ExcelName);
if (!File.Exists(excelPath))
{
allMessages.Add($"[{target.ExcelName}] 文件不存在,跳过");
continue;
}
var tableSO = allArtTables.FirstOrDefault(x => x.TableName == target.SOTableName);
if (tableSO == null)
{
allMessages.Add($"[{target.ExcelName}] 未找到 ArtTableSO: {target.SOTableName},跳过");
totalErrors++;
continue;
}
// 备份
string backupPath = excelPath + ".bak";
File.Copy(excelPath, backupPath, true);
int migrated = 0, skipped = 0, errors = 0;
using (var package = new ExcelPackage(new FileInfo(excelPath)))
{
var ws = package.Workbook.Worksheets[target.SheetName];
if (ws == null)
{
allMessages.Add($"[{target.ExcelName}] 未找到 Sheet: {target.SheetName},跳过");
totalErrors++;
continue;
}
// 找 Icon 列和 Id 列
int iconCol = -1, idCol = -1;
int colCount = ws.Dimension?.Columns ?? 0;
for (int c = 1; c <= colCount; c++)
{
string h = ws.Cells[1, c].Text;
if (h == "Icon") iconCol = c;
else if (h == "Id") idCol = c;
}
if (iconCol < 0)
{
allMessages.Add($"[{target.ExcelName}] 未找到 Icon 列,跳过");
totalErrors++;
continue;
}
int rowCount = ws.Dimension?.Rows ?? 0;
for (int row = 3; row <= rowCount; row++)
{
string iconText = ws.Cells[row, iconCol].Text.Trim();
if (string.IsNullOrEmpty(iconText))
{
continue;
}
// 判断是否是旧格式(纯数字)
if (!int.TryParse(iconText, out int artItemId))
{
// 已经是Name格式
skipped++;
continue;
}
string rowId = idCol > 0 ? ws.Cells[row, idCol].Text : row.ToString();
var artItem = tableSO.Items.FirstOrDefault(x => x.Id == artItemId);
if (artItem == null)
{
errors++;
allMessages.Add($"[{target.ExcelName}] Row {row} Id={rowId}: {target.SOTableName} 中无 ArtItemId={artItemId}");
continue;
}
ws.Cells[row, iconCol].Value = artItem.Name;
migrated++;
}
package.Save();
}
totalMigrated += migrated;
totalSkipped += skipped;
totalErrors += errors;
allMessages.Add($"[{target.ExcelName}] 成功: {migrated}, 跳过: {skipped}, 错误: {errors} (备份: {backupPath})");
}
// 报告
string msg = $"Icon 迁移完成!\n\n" +
$"总计成功: {totalMigrated} 条\n" +
$"已是新格式(跳过): {totalSkipped} 条\n" +
$"错误: {totalErrors} 条\n\n" +
string.Join("\n", allMessages);
EditorUtility.DisplayDialog("迁移结果", msg, "确定");
Debug.Log(msg);
}
}
}