Design_SubModule/Scripts/Editor/Design_Tools/BaseDesignToolEditor.cs
2026-02-27 19:20:28 +08:00

516 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace DesignTools
{
/// <summary>
/// 策划工具编辑器基类
/// 提供通用的Docs路径管理、Git操作、UI框架等功能
/// </summary>
public abstract class BaseDesignToolEditor : EditorWindow
{
#region
/// <summary>
/// Design_SubModule相对路径
/// </summary>
protected const string DESIGN_SUBMODULE_PATH = "Assets/Design_SubModule";
#endregion
#region
/// <summary>
/// Docs项目根目录路径
/// </summary>
protected string docsRootPath = "";
/// <summary>
/// 数据是否已加载
/// </summary>
protected bool isDataLoaded = false;
/// <summary>
/// 滚动位置
/// </summary>
protected Vector2 scrollPosition;
#endregion
#region
/// <summary>
/// 获取EditorPrefs保存路径的Key
/// 子类需要返回唯一的Key例如: "ItemConfigEditor_DocsPath"
/// </summary>
protected abstract string GetDocsPathPrefKey();
/// <summary>
/// 获取工具窗口标题
/// </summary>
protected abstract string GetWindowTitle();
/// <summary>
/// 加载具体的配置数据
/// 在Git检查和更新完成后调用
/// </summary>
protected abstract void LoadConfigData();
/// <summary>
/// 绘制数据编辑器UI
/// 在数据加载完成后调用
/// </summary>
protected abstract void DrawDataEditor();
/// <summary>
/// 获取工具窗口的最小尺寸
/// 默认为 (1000, 600)
/// </summary>
protected virtual Vector2 GetMinWindowSize()
{
return new Vector2(1000, 600);
}
/// <summary>
/// 是否需要检查Design_SubModule分支
/// 默认为true子类可以覆盖
/// </summary>
protected virtual bool NeedCheckDesignSubModuleBranch()
{
return true;
}
/// <summary>
/// 获取Docs配置路径相对于Docs根目录的路径
/// 默认为 "config",子类可以覆盖
/// </summary>
protected virtual string GetDocsConfigPath()
{
return "config";
}
/// <summary>
/// 是否显示底部操作按钮(保存、重新加载等)
/// 默认为true
/// </summary>
protected virtual bool ShowBottomButtons()
{
return true;
}
/// <summary>
/// 保存数据到Excel
/// 子类可以覆盖此方法实现具体的保存逻辑
/// </summary>
protected virtual void SaveDataToExcel()
{
EditorUtility.DisplayDialog("提示", "此工具未实现保存功能", "确定");
}
#endregion
#region Unity生命周期
protected virtual void OnEnable()
{
// 读取上次保存的路径
string prefKey = GetDocsPathPrefKey();
if (EditorPrefs.HasKey(prefKey))
{
docsRootPath = EditorPrefs.GetString(prefKey);
}
}
protected virtual void OnGUI()
{
// 绘制工具栏
DrawToolbar();
EditorGUILayout.Space(5);
// 绘制Docs路径选择区域
DrawDocsPathSelector();
EditorGUILayout.Space(5);
// 绘制数据编辑区域或提示信息
if (isDataLoaded)
{
DrawDataEditor();
// 绘制底部操作按钮
if (ShowBottomButtons())
{
DrawBottomButtons();
}
}
else
{
EditorGUILayout.HelpBox("请先选择Docs根目录并加载配置数据", MessageType.Info);
}
}
#endregion
#region UI绘制方法
/// <summary>
/// 绘制工具栏
/// </summary>
protected virtual void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
EditorGUILayout.LabelField(GetWindowTitle(), EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
/// <summary>
/// 绘制Docs路径选择区域
/// </summary>
protected virtual void DrawDocsPathSelector()
{
EditorGUILayout.BeginVertical("box");
EditorGUILayout.LabelField("Docs项目根目录", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
docsRootPath = EditorGUILayout.TextField("路径", docsRootPath);
if (GUILayout.Button("选择文件夹", GUILayout.Width(100)))
{
SelectDocsPath();
}
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("加载配置数据", GUILayout.Height(30)))
{
LoadData();
}
EditorGUILayout.EndVertical();
}
/// <summary>
/// 绘制底部操作按钮
/// </summary>
protected virtual void DrawBottomButtons()
{
EditorGUILayout.Space(5);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("保存到Excel", GUILayout.Height(30), GUILayout.Width(150)))
{
SaveDataToExcel();
}
if (GUILayout.Button("重新加载", GUILayout.Height(30), GUILayout.Width(150)))
{
LoadData();
}
EditorGUILayout.EndHorizontal();
}
#endregion
#region
/// <summary>
/// 选择Docs路径
/// </summary>
protected virtual void SelectDocsPath()
{
string selectedPath = EditorUtility.OpenFolderPanel("选择Docs项目根目录", "", "");
if (!string.IsNullOrEmpty(selectedPath))
{
docsRootPath = selectedPath;
EditorPrefs.SetString(GetDocsPathPrefKey(), docsRootPath);
}
}
/// <summary>
/// 加载数据(最终方法,不可覆盖)
/// </summary>
protected void LoadData()
{
try
{
// 1. 校验路径
if (!ValidateDocsPath())
{
return;
}
// 2. 检查Docs是否为Git仓库并更新
if (!CheckAndUpdateDocsRepository())
{
return;
}
// 3. 检查Design_SubModule分支如果需要
if (NeedCheckDesignSubModuleBranch())
{
if (!CheckAndSwitchDesignSubModuleBranch())
{
return;
}
}
// 4. 调用子类的具体加载逻辑
LoadConfigData();
// 5. 设置数据加载完成标志
isDataLoaded = true;
// 6. 显示成功提示
EditorUtility.DisplayDialog("成功", "配置数据加载成功!", "确定");
}
catch (Exception e)
{
EditorUtility.DisplayDialog("错误", $"加载数据失败: {e.Message}\n{e.StackTrace}", "确定");
isDataLoaded = false;
}
}
/// <summary>
/// 校验Docs路径
/// </summary>
protected virtual bool ValidateDocsPath()
{
if (string.IsNullOrEmpty(docsRootPath) || !Directory.Exists(docsRootPath))
{
EditorUtility.DisplayDialog("错误", "请选择有效的Docs根目录", "确定");
return false;
}
return true;
}
#endregion
#region Git操作方法
/// <summary>
/// 执行Git命令
/// </summary>
/// <param name="workingDirectory">工作目录</param>
/// <param name="arguments">Git命令参数</param>
/// <param name="success">是否执行成功</param>
/// <returns>命令输出或错误信息</returns>
protected 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}";
}
}
/// <summary>
/// 检查并更新Docs仓库
/// </summary>
protected virtual bool CheckAndUpdateDocsRepository()
{
// 1. 检查是否为Git仓库
string gitPath = Path.Combine(docsRootPath, ".git");
if (!Directory.Exists(gitPath))
{
EditorUtility.DisplayDialog("错误",
$"Docs目录不是Git仓库\n路径: {docsRootPath}\n\n请确保Docs项目已正确克隆",
"确定");
return false;
}
// 2. 执行Git Fetch
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;
}
// 3. 检查仓库状态
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;
}
// 4. 检查是否有未提交的更改
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;
}
}
// 5. 如果远程有更新则执行Pull
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;
}
/// <summary>
/// 检查并切换Design_SubModule到main分支
/// </summary>
protected virtual 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 分支");
}
else
{
EditorUtility.ClearProgressBar();
Debug.Log("Design_SubModule已在main分支");
}
return true;
}
#endregion
#region
/// <summary>
/// 获取Docs配置文件的完整路径
/// </summary>
/// <param name="fileName">文件名(包含扩展名)</param>
/// <returns>完整路径</returns>
protected string GetDocsConfigFilePath(string fileName)
{
return Path.Combine(docsRootPath, GetDocsConfigPath(), fileName);
}
/// <summary>
/// 获取Docs根目录下的完整路径
/// </summary>
/// <param name="relativePath">相对路径</param>
/// <returns>完整路径</returns>
protected string GetDocsFullPath(string relativePath)
{
return Path.Combine(docsRootPath, relativePath);
}
#endregion
}
}