using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor.PackageManager;
using Newtonsoft.Json;
namespace AutoUI
{
///
/// AutoUI 主窗口 - 对应原 panel.js
///
public class AutoUIWindow : EditorWindow
{
[SerializeField] private VisualTreeAsset m_UXML;
// 服务实例
private AIService _aiService;
private AuthService _authService;
private PrefabBuilder _prefabBuilder;
private UpdateService _updateService;
// UI 元素引用 - 主界面
private TextField _imagePathField;
private TextField _texturePathField;
private Button _browseImageBtn;
private Button _browseTextureBtn;
private Button _generateBtn;
private Label _statusLabel;
private ScrollView _logScrollView;
// 图片拖放区域(需要在选图前后切换状态)
private VisualElement _imageDropZone;
private Image _dropZoneImage;
private Label _dropZoneText;
private Label _dropZoneInfo;
// 切图文件夹拖放区域(需要在选文件夹前后切换状态)
private VisualElement _textureDropZone;
private Label _textureDropText;
private Label _textureDropPath;
private Label _textureDropCount;
// 授权工具栏
private VisualElement _authToolbar;
private Label _toolbarBadge;
private Label _quotaText;
private Button _toolbarUpgradeBtn;
private Button _toolbarMenuBtn;
// 当前授权状态
private AuthResult _currentAuthState;
private bool _serverOnline;
// UI 元素引用 - 面板
private VisualElement _authPanel;
private VisualElement _mainPanel;
// UI 元素引用 - Tab导航
private Button _tabBtnGenerate;
private Button _tabBtnSettings;
private Button _tabBtnFeedback;
private VisualElement _tabGenerate;
private VisualElement _tabSettings;
private VisualElement _tabFeedback;
// UI 元素引用 - 设置Tab
private VisualElement _commonFoldersContainer;
private Button _addCommonFolderBtn;
private Label _commonFoldersHint;
private Label _folderCountBadge; // 文件夹数量徽章
// 公共文件夹列表
private List _commonFolders = new List();
private const int MAX_COMMON_FOLDERS = 5;
// UI 元素引用 - 登录
private VisualElement _loginPanel;
private TextField _loginEmailField;
private TextField _loginPasswordField;
private Button _loginBtn;
private Button _showRegisterBtn;
private Button _forgotPasswordBtn; // 【新增】忘记密码按钮
private Label _loginErrorLabel; // 【新增】登录错误提示
// UI 元素引用 - 注册
private VisualElement _registerPanel;
private TextField _regEmailField;
private TextField _regCodeField;
private TextField _regPasswordField;
private TextField _regConfirmPasswordField;
private Button _sendCodeBtn;
private Button _registerBtn;
private Button _showLoginBtn;
private Label _codeHintLabel;
private Label _registerErrorLabel; // 【新增】注册错误提示
// 【新增】UI 元素引用 - 重置密码
private VisualElement _resetPasswordPanel;
private TextField _resetEmailField;
private TextField _resetCodeField;
private TextField _resetNewPasswordField;
private TextField _resetConfirmPasswordField;
private Button _resetSendCodeBtn;
private Button _resetPasswordBtn;
private Button _resetBackToLoginBtn;
private Label _resetCodeHintLabel;
private Label _resetErrorLabel; // 【新增】重置密码错误提示
private bool _isSendingCode = false;
private bool _isGenerating = false;
private List _textureFiles = new List();
// 【新增】防连点机制
private Dictionary _lastClickTime = new Dictionary();
private const int COOLDOWN_SECONDS = 3; // 3秒冷却期
// 【新增】树形视图、JSON视图
private VisualElement _resultViewContainer;
private TextField _jsonViewField;
private ScrollView _jsonScrollView; // JSON视图的滚动容器
private VisualElement _treeViewContainer;
private ToolbarMenu _viewModeMenu;
private enum ViewMode { Json, Tree }
private ViewMode _currentViewMode = ViewMode.Json;
private UUIStructure _currentUIStructure;
private HashSet _expandedTreeNodes = new HashSet();
private string _selectedTreeNodePath;
// 【新增】反馈Tab
private TextField _feedbackTextField;
private Button _feedbackSubmitBtn;
private Label _feedbackHintLabel;
private VisualElement _feedbackSuccessContainer;
private Button _feedbackErrorBtn; // 状态栏错误反馈按钮
// 【新增】Tab状态
private enum TabType { Generate, Settings, Feedback }
private TabType _currentTab = TabType.Generate;
// 【新增】加载动画相关
private VisualElement _loadingOverlay;
private VisualElement _loadingSpinner;
private Label _loadingText;
private int _loadingDots = 0;
private long _loadingStartTime = 0;
private float _spinnerRotation = 0f;
// 【新增】错误反馈状态
private Exception _lastError;
private bool _errorFeedbackSubmitted;
// 【新增】DEBUG开关 - 控制生成结果显示
private const bool DEBUG_SHOW_RESULT = false;
// 【新增】分析模式选择
private AnalysisMode _currentAnalysisMode = AnalysisMode.UnityNative;
private UnityPrefabBuilder _unityPrefabBuilder;
private UnityPrefabNode _currentUnityPrefab; // 【新增】保存Unity原生模式结果
// EditorPrefs keys
private const string PREFS_TEXTURE_FOLDER_PATH = "AutoUI_TextureFolderPath";
private const string PREFS_LAST_IMAGE_DIR = "AutoUI_LastImageDir";
private const string PREFS_QUICK_MODE_IMAGE = "AutoUI_QuickMode_Image";
private const string PREFS_QUICK_MODE_TEXTURE = "AutoUI_QuickMode_Texture";
// 【新增】状态栏容器(根级别,始终显示)
private VisualElement _statusBarContainer;
private VisualElement _statusDot;
public static void ShowWindow()
{
var window = GetWindow();
window.titleContent = new GUIContent("AutoUI界面生成器");
window.minSize = new Vector2(450, 700);
}
private void CreateGUI()
{
// 加载UXML
var root = rootVisualElement;
// 如果没有指定UXML,创建默认UI
if (m_UXML == null)
{
CreateDefaultUI(root);
}
else
{
m_UXML.CloneTree(root);
}
// 绑定UI事件
BindUIElements();
// 【关键】此时默认面板已显示,再开始同步初始化
// 初始化过程中状态栏会显示进度
InitializeSync();
}
///
/// 【修复】同步初始化流程
/// 每一步都是同步调用,避免异步带来的复杂性
///
private void InitializeSync()
{
try
{
Log("=== AutoUI 同步初始化开始 ===");
// 1. 同步初始化服务
Log("[1/4] 初始化服务...");
UpdateStatus("初始化服务中...", StatusColor.Info);
InitializeServicesSync();
// 2. 同步检查服务器状态
Log("[2/4] 检查服务器状态...");
UpdateStatus("检查服务器状态...", StatusColor.Info);
bool serverOk = CheckServerStatusSync();
if (!serverOk)
{
LogError("服务器连接失败,初始化终止");
UpdateStatus("初始化失败:服务器连接失败", StatusColor.Error);
ShowErrorFeedbackButton(new Exception("服务器连接失败"));
return;
}
// 3. 同步检查授权状态
Log("[3/4] 检查授权状态...");
UpdateStatus("检查授权状态...", StatusColor.Info);
RefreshAuthStateSync();
// 4. 更新UI
Log("[4/4] 更新界面状态...");
UpdateUIBasedOnState();
UpdateStatus("准备就绪", StatusColor.Success);
// 【首次体验优化】5. 检查是否首次使用
ShowFirstTimeGuide();
// 6. 延迟检查更新(异步,不阻塞)
_ = Task.Run(async () =>
{
await Task.Delay(2000);
try
{
await CheckForUpdatesAsync();
}
catch (Exception ex)
{
Debug.LogWarning($"[AutoUI] 自动检查更新失败: {ex.Message}");
}
});
Log("=== AutoUI 同步初始化完成 ===");
}
catch (Exception ex)
{
LogError($"初始化失败: {ex.Message}");
UpdateStatus($"初始化失败: {ex.Message}", StatusColor.Error);
ShowErrorFeedbackButton(ex);
Debug.LogException(ex);
}
}
///
/// 同步初始化服务(后台线程网络请求,主线程处理结果)
///
private void InitializeServicesSync()
{
try
{
// 【主线程】获取设备指纹和用户数据
Log("获取设备指纹...");
var deviceResult = DeviceFingerprint.Generate();
var deviceInfo = DeviceFingerprint.GetInfo();
// 【新增】加载公共文件夹设置
LoadCommonFolders();
string savedToken = EditorPrefs.GetString("autoui_token", "");
string savedUserInfo = EditorPrefs.GetString("autoui_user_info", "");
string savedUsername = "未知用户";
if (!string.IsNullOrEmpty(savedUserInfo))
{
try
{
var userInfo = JsonUtility.FromJson(savedUserInfo);
savedUsername = userInfo?.email ?? "未知用户";
}
catch { }
}
// 【后台线程】执行网络初始化
_authService = new AuthService();
_authService.PreInitialize(deviceResult, deviceInfo, savedToken, savedUsername);
Task.Run(() => _authService.InitializeSync()).GetAwaiter().GetResult();
_aiService = new AIService(_authService);
_prefabBuilder = new PrefabBuilder();
_updateService = new UpdateService();
Task.Run(() => _updateService.InitializeAsync()).GetAwaiter().GetResult();
Log("服务初始化完成");
}
catch (Exception ex)
{
LogError($"服务初始化失败: {ex.Message}");
throw;
}
}
///
/// 同步检查服务器状态(后台线程网络请求)
///
private bool CheckServerStatusSync()
{
try
{
var status = Task.Run(() => _authService.CheckServerStatusSync()).GetAwaiter().GetResult();
_serverOnline = status.online;
if (status.online)
{
Log($"服务器状态: 在线 (版本: {status.version})");
return true;
}
else
{
LogError($"服务器状态: 离线 ({status.error})");
return false;
}
}
catch (Exception ex)
{
_serverOnline = false;
LogError($"服务器检查失败: {ex.Message}");
return false;
}
}
///
/// 同步刷新授权状态(后台线程网络请求,主线程处理结果)
///
private void RefreshAuthStateSync(bool forceRefresh = false)
{
try
{
// 如果强制刷新,清除缓存
if (forceRefresh)
{
_authService?.ClearAuthCache();
}
// 使用 AuthService.Token(已在主线程初始化时缓存)
var authState = Task.Run(() => _authService.GetAuthSync(_authService.Token)).GetAwaiter().GetResult();
_currentAuthState = authState;
// 调试日志
Log($"授权状态刷新完成: Type={authState.Type}, Valid={authState.Valid}, Credits={authState.Credits}");
}
catch (Exception ex)
{
LogError($"刷新授权状态失败: {ex.Message}");
}
}
///
/// 【新增】状态颜色枚举
///
private enum StatusColor
{
Info, // 蓝色
Success, // 绿色
Warning, // 黄色
Error // 红色
}
///
/// 【新增】更新状态栏(带颜色)
///
private void UpdateStatus(string message, StatusColor color)
{
if (_statusLabel == null) return;
_statusLabel.text = message;
// 更新状态点颜色
if (_statusDot != null)
{
_statusDot.style.backgroundColor = color switch
{
StatusColor.Success => new Color(0.2f, 0.8f, 0.2f),
StatusColor.Warning => new Color(1f, 0.8f, 0f),
StatusColor.Error => new Color(1f, 0.3f, 0.3f),
_ => new Color(0.4f, 0.6f, 1f)
};
}
}
private void CreateDefaultUI(VisualElement root)
{
root.style.paddingTop = 0;
root.style.paddingLeft = 0;
root.style.paddingRight = 0;
root.style.paddingBottom = 0;
root.style.backgroundColor = new Color(0.12f, 0.12f, 0.14f);
// ===== 标题栏 =====
var headerContainer = new VisualElement()
{
style =
{
flexDirection = FlexDirection.Row,
alignItems = Align.Center,
paddingTop = 14,
paddingBottom = 10,
paddingLeft = 14,
paddingRight = 14,
backgroundColor = new Color(0.15f, 0.15f, 0.17f)
}
};
var title = new Label("AutoUI")
{
style = {
fontSize = 18,
unityFontStyleAndWeight = FontStyle.Bold,
color = new Color(0.95f, 0.95f, 0.95f)
}
};
headerContainer.Add(title);
var versionLabel = new Label($" v{Config.Version}")
{
style = {
fontSize = 12,
color = new Color(0.6f, 0.6f, 0.6f),
marginTop = 3
}
};
headerContainer.Add(versionLabel);
// 弹性空间
headerContainer.Add(new VisualElement() { style = { flexGrow = 1 } });
// 菜单按钮
_toolbarMenuBtn = new Button() { text = "⋮", style = { width = 28, height = 28, fontSize = 16 } };
_toolbarMenuBtn.style.backgroundColor = Color.clear;
_toolbarMenuBtn.style.borderLeftWidth = 0;
_toolbarMenuBtn.style.borderRightWidth = 0;
_toolbarMenuBtn.style.borderTopWidth = 0;
_toolbarMenuBtn.style.borderBottomWidth = 0;
_toolbarMenuBtn.style.borderTopLeftRadius = 4;
_toolbarMenuBtn.style.borderTopRightRadius = 4;
_toolbarMenuBtn.style.borderBottomLeftRadius = 4;
_toolbarMenuBtn.style.borderBottomRightRadius = 4;
_toolbarMenuBtn.style.color = new Color(0.75f, 0.75f, 0.75f);
headerContainer.Add(_toolbarMenuBtn);
root.Add(headerContainer);
// 认证面板容器
_authPanel = new VisualElement()
{
style =
{
paddingTop = 40,
paddingBottom = 20,
paddingLeft = 30,
paddingRight = 30
}
};
// ===== 登录面板 =====
_loginPanel = new VisualElement()
{
style =
{
display = DisplayStyle.None,
backgroundColor = new Color(0.12f, 0.12f, 0.14f),
borderTopLeftRadius = 8,
borderTopRightRadius = 8,
borderBottomLeftRadius = 8,
borderBottomRightRadius = 8,
paddingTop = 25,
paddingBottom = 25,
paddingLeft = 25,
paddingRight = 25,
marginBottom = 10
}
};
var loginTitle = new Label("用户登录")
{
style =
{
unityFontStyleAndWeight = FontStyle.Bold,
fontSize = 20,
color = new Color(0.95f, 0.95f, 0.95f),
marginBottom = 20,
unityTextAlign = TextAnchor.MiddleCenter
}
};
_loginPanel.Add(loginTitle);
_loginEmailField = new TextField("邮箱")
{
value = "",
style =
{
height = 36,
fontSize = 14,
marginBottom = 12,
marginTop = 8
}
};
_loginPanel.Add(_loginEmailField);
_loginPasswordField = new TextField("密码")
{
isPasswordField = true,
value = "",
style =
{
height = 36,
fontSize = 14,
marginBottom = 12,
marginTop = 8
}
};
_loginPanel.Add(_loginPasswordField);
var loginBtnRow = new VisualElement()
{
style = { flexDirection = FlexDirection.Row, marginTop = 15 }
};
_loginBtn = new Button()
{
text = "登录",
style =
{
flexGrow = 1,
height = 36,
fontSize = 14,
unityFontStyleAndWeight = FontStyle.Bold
}
};
_showRegisterBtn = new Button()
{
text = "注册账号",
style =
{
marginLeft = 10,
height = 36,
width = 90
}
};
loginBtnRow.Add(_loginBtn);
loginBtnRow.Add(_showRegisterBtn);
_loginPanel.Add(loginBtnRow);
// 【新增】登录错误提示
_loginErrorLabel = new Label("")
{
style =
{
color = new Color(1f, 0.3f, 0.3f),
fontSize = 12,
marginTop = 8,
display = DisplayStyle.None,
unityTextAlign = TextAnchor.MiddleCenter
}
};
_loginPanel.Add(_loginErrorLabel);
// 忘记密码链接
_forgotPasswordBtn = new Button()
{
text = "忘记密码?",
style =
{
marginTop = 12,
height = 24,
color = new Color(0.3f, 0.7f, 1f),
backgroundColor = Color.clear,
borderLeftColor = Color.clear,
borderRightColor = Color.clear,
borderTopColor = Color.clear,
borderBottomColor = Color.clear,
unityTextAlign = TextAnchor.MiddleCenter
}
};
_loginPanel.Add(_forgotPasswordBtn);
// 返回主界面按钮
var backToMainBtn = new Button()
{
text = "← 返回主界面",
style =
{
marginTop = 15,
height = 28,
color = new Color(0.6f, 0.6f, 0.6f),
backgroundColor = Color.clear,
borderLeftColor = Color.clear,
borderRightColor = Color.clear,
borderTopColor = Color.clear,
borderBottomColor = Color.clear,
unityTextAlign = TextAnchor.MiddleCenter,
fontSize = 12
}
};
backToMainBtn.clicked += () =>
{
_authPanel.style.display = DisplayStyle.None;
_mainPanel.style.display = DisplayStyle.Flex;
};
_loginPanel.Add(backToMainBtn);
_authPanel.Add(_loginPanel);
// ===== 注册面板 =====
_registerPanel = new VisualElement()
{
style =
{
display = DisplayStyle.None,
backgroundColor = new Color(0.12f, 0.12f, 0.14f),
borderTopLeftRadius = 8,
borderTopRightRadius = 8,
borderBottomLeftRadius = 8,
borderBottomRightRadius = 8,
paddingTop = 25,
paddingBottom = 25,
paddingLeft = 25,
paddingRight = 25,
marginBottom = 10
}
};
var regTitle = new Label("注册账号")
{
style =
{
unityFontStyleAndWeight = FontStyle.Bold,
fontSize = 20,
color = new Color(0.95f, 0.95f, 0.95f),
marginBottom = 20,
unityTextAlign = TextAnchor.MiddleCenter
}
};
_registerPanel.Add(regTitle);
_regEmailField = new TextField("邮箱")
{
style = { height = 36, fontSize = 14, marginBottom = 12, marginTop = 8 }
};
_registerPanel.Add(_regEmailField);
// 验证码行
var codeRow = new VisualElement()
{
style = { flexDirection = FlexDirection.Row, marginBottom = 12, alignItems = Align.FlexEnd }
};
_regCodeField = new TextField("验证码")
{
style = { flexGrow = 1, height = 36, fontSize = 14, marginTop = 8 }
};
_sendCodeBtn = new Button()
{
text = "发送",
style = { width = 80, marginLeft = 8, height = 36 }
};
codeRow.Add(_regCodeField);
codeRow.Add(_sendCodeBtn);
_registerPanel.Add(codeRow);
_codeHintLabel = new Label("")
{
style = { color = new Color(0.6f, 0.6f, 0.6f), fontSize = 11, marginTop = -5, marginBottom = 8 }
};
_registerPanel.Add(_codeHintLabel);
_regPasswordField = new TextField("密码")
{
isPasswordField = true,
style = { height = 36, fontSize = 14, marginBottom = 12, marginTop = 8 }
};
_registerPanel.Add(_regPasswordField);
_regConfirmPasswordField = new TextField("确认密码")
{
isPasswordField = true,
style = { height = 36, fontSize = 14, marginBottom = 12, marginTop = 8 }
};
_registerPanel.Add(_regConfirmPasswordField);
var regBtnRow = new VisualElement()
{
style = { flexDirection = FlexDirection.Row, marginTop = 15 }
};
_registerBtn = new Button()
{
text = "注册",
style = { flexGrow = 1, height = 36, fontSize = 14, unityFontStyleAndWeight = FontStyle.Bold }
};
_showLoginBtn = new Button()
{
text = "返回登录",
style = { marginLeft = 10, height = 36, width = 90 }
};
regBtnRow.Add(_registerBtn);
regBtnRow.Add(_showLoginBtn);
_registerPanel.Add(regBtnRow);
// 【新增】注册错误提示
_registerErrorLabel = new Label("")
{
style =
{
color = new Color(1f, 0.3f, 0.3f),
fontSize = 12,
marginTop = 8,
display = DisplayStyle.None,
unityTextAlign = TextAnchor.MiddleCenter
}
};
_registerPanel.Add(_registerErrorLabel);
// 返回主界面按钮
var regBackToMainBtn = new Button()
{
text = "← 返回主界面",
style =
{
marginTop = 15,
height = 28,
color = new Color(0.6f, 0.6f, 0.6f),
backgroundColor = Color.clear,
borderLeftColor = Color.clear,
borderRightColor = Color.clear,
borderTopColor = Color.clear,
borderBottomColor = Color.clear,
unityTextAlign = TextAnchor.MiddleCenter,
fontSize = 12
}
};
regBackToMainBtn.clicked += () =>
{
_authPanel.style.display = DisplayStyle.None;
_mainPanel.style.display = DisplayStyle.Flex;
};
_registerPanel.Add(regBackToMainBtn);
_authPanel.Add(_registerPanel);
// ===== 重置密码面板 =====
_resetPasswordPanel = new VisualElement()
{
style =
{
display = DisplayStyle.None,
backgroundColor = new Color(0.12f, 0.12f, 0.14f),
borderTopLeftRadius = 8,
borderTopRightRadius = 8,
borderBottomLeftRadius = 8,
borderBottomRightRadius = 8,
paddingTop = 25,
paddingBottom = 25,
paddingLeft = 25,
paddingRight = 25,
marginBottom = 10
}
};
var resetTitle = new Label("重置密码")
{
style =
{
unityFontStyleAndWeight = FontStyle.Bold,
fontSize = 20,
color = new Color(0.95f, 0.95f, 0.95f),
marginBottom = 20,
unityTextAlign = TextAnchor.MiddleCenter
}
};
_resetPasswordPanel.Add(resetTitle);
_resetEmailField = new TextField("邮箱")
{
style = { height = 36, fontSize = 14, marginBottom = 12, marginTop = 8 }
};
_resetPasswordPanel.Add(_resetEmailField);
// 验证码行
var resetCodeRow = new VisualElement()
{
style = { flexDirection = FlexDirection.Row, marginBottom = 12, alignItems = Align.FlexEnd }
};
_resetCodeField = new TextField("验证码")
{
style = { flexGrow = 1, height = 36, fontSize = 14, marginTop = 8 }
};
_resetSendCodeBtn = new Button()
{
text = "发送",
style = { width = 80, marginLeft = 8, height = 36 }
};
resetCodeRow.Add(_resetCodeField);
resetCodeRow.Add(_resetSendCodeBtn);
_resetPasswordPanel.Add(resetCodeRow);
_resetCodeHintLabel = new Label("")
{
style = { color = new Color(0.6f, 0.6f, 0.6f), fontSize = 11, marginTop = -5, marginBottom = 8 }
};
_resetPasswordPanel.Add(_resetCodeHintLabel);
_resetNewPasswordField = new TextField("新密码")
{
isPasswordField = true,
style = { height = 36, fontSize = 14, marginBottom = 12, marginTop = 8 }
};
_resetPasswordPanel.Add(_resetNewPasswordField);
_resetConfirmPasswordField = new TextField("确认新密码")
{
isPasswordField = true,
style = { height = 36, fontSize = 14, marginBottom = 12, marginTop = 8 }
};
_resetPasswordPanel.Add(_resetConfirmPasswordField);
var resetBtnRow = new VisualElement()
{
style = { flexDirection = FlexDirection.Row, marginTop = 15 }
};
_resetPasswordBtn = new Button()
{
text = "重置密码",
style = { flexGrow = 1, height = 36, fontSize = 14, unityFontStyleAndWeight = FontStyle.Bold }
};
_resetBackToLoginBtn = new Button()
{
text = "返回登录",
style = { marginLeft = 10, height = 36, width = 90 }
};
resetBtnRow.Add(_resetPasswordBtn);
resetBtnRow.Add(_resetBackToLoginBtn);
_resetPasswordPanel.Add(resetBtnRow);
// 【新增】重置密码错误提示
_resetErrorLabel = new Label("")
{
style =
{
color = new Color(1f, 0.3f, 0.3f),
fontSize = 12,
marginTop = 8,
display = DisplayStyle.None,
unityTextAlign = TextAnchor.MiddleCenter
}
};
_resetPasswordPanel.Add(_resetErrorLabel);
// 返回主界面按钮
var resetBackToMainBtn = new Button()
{
text = "← 返回主界面",
style =
{
marginTop = 15,
height = 28,
color = new Color(0.6f, 0.6f, 0.6f),
backgroundColor = Color.clear,
borderLeftColor = Color.clear,
borderRightColor = Color.clear,
borderTopColor = Color.clear,
borderBottomColor = Color.clear,
unityTextAlign = TextAnchor.MiddleCenter,
fontSize = 12
}
};
resetBackToMainBtn.clicked += () =>
{
_authPanel.style.display = DisplayStyle.None;
_mainPanel.style.display = DisplayStyle.Flex;
};
_resetPasswordPanel.Add(resetBackToMainBtn);
_authPanel.Add(_resetPasswordPanel);
root.Add(_authPanel);
// 主面板容器
_mainPanel = new VisualElement()
{
style =
{
paddingLeft = 14,
paddingRight = 14,
paddingTop = 12,
paddingBottom = 14
}
};
// ===== 授权工具栏(顶部,登录后始终显示)=====
_authToolbar = new VisualElement()
{
style =
{
flexDirection = FlexDirection.Row,
backgroundColor = new Color(0.18f, 0.18f, 0.2f),
paddingLeft = 12,
paddingRight = 12,
paddingTop = 8,
paddingBottom = 8,
marginBottom = 15,
borderBottomLeftRadius = 8,
borderBottomRightRadius = 8,
borderTopLeftRadius = 8,
borderTopRightRadius = 8,
alignItems = Align.Center,
display = DisplayStyle.None // 默认隐藏,登录后显示
}
};
// 套餐徽章
_toolbarBadge = new Label("畅享版")
{
style =
{
backgroundColor = new Color(0.25f, 0.55f, 0.95f),
color = Color.white,
paddingLeft = 10,
paddingRight = 10,
paddingTop = 4,
paddingBottom = 4,
borderTopLeftRadius = 4,
borderTopRightRadius = 4,
borderBottomLeftRadius = 4,
borderBottomRightRadius = 4,
fontSize = 12
}
};
_authToolbar.Add(_toolbarBadge);
// 额度显示
var quotaContainer = new VisualElement() { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center } };
quotaContainer.Add(new Label("剩余 ") { style = { color = new Color(0.92f, 0.92f, 0.92f), fontSize = 13 } });
_quotaText = new Label("0")
{
style =
{
color = new Color(0.3f, 0.9f, 0.4f),
fontSize = 14,
unityFontStyleAndWeight = FontStyle.Bold
}
};
quotaContainer.Add(_quotaText);
_authToolbar.Add(quotaContainer);
// 弹性空间
_authToolbar.Add(new VisualElement() { style = { flexGrow = 1 } });
// 升级按钮
_toolbarUpgradeBtn = new Button() { text = "升级" };
_toolbarUpgradeBtn.style.backgroundColor = new Color(0.95f, 0.6f, 0.1f);
_toolbarUpgradeBtn.style.borderTopLeftRadius = 6;
_toolbarUpgradeBtn.style.borderBottomLeftRadius = 6;
_toolbarUpgradeBtn.style.borderTopRightRadius = 6;
_toolbarUpgradeBtn.style.borderBottomRightRadius = 6;
_toolbarUpgradeBtn.style.color = Color.white;
_toolbarUpgradeBtn.style.fontSize = 12;
_toolbarUpgradeBtn.clicked += ShowSubscribePanel;
_authToolbar.Add(_toolbarUpgradeBtn);
// 菜单按钮(工具栏内)
var toolbarMenuBtn = new Button() { text = "⋮", style = { marginLeft = 8, width = 24, height = 24, fontSize = 14 } };
toolbarMenuBtn.style.backgroundColor = Color.clear;
toolbarMenuBtn.style.borderLeftWidth = 0;
toolbarMenuBtn.style.borderRightWidth = 0;
toolbarMenuBtn.style.borderTopWidth = 0;
toolbarMenuBtn.style.borderBottomWidth = 0;
toolbarMenuBtn.style.color = new Color(0.8f, 0.8f, 0.8f);
toolbarMenuBtn.clicked += OnLogoutClicked;
_authToolbar.Add(toolbarMenuBtn);
_mainPanel.Add(_authToolbar);
// ===== Tab导航(简洁下划线样式)=====
var tabNav = new VisualElement()
{
style =
{
flexDirection = FlexDirection.Row,
marginBottom = 16
}
};
_tabBtnGenerate = new Button() { text = "生成界面" };
_tabBtnGenerate.style.backgroundColor = Color.clear;
_tabBtnGenerate.style.borderLeftWidth = 0;
_tabBtnGenerate.style.borderRightWidth = 0;
_tabBtnGenerate.style.borderTopWidth = 0;
_tabBtnGenerate.style.borderBottomWidth = 2;
_tabBtnGenerate.style.borderBottomColor = new Color(0.25f, 0.55f, 0.95f);
_tabBtnGenerate.style.borderTopLeftRadius = 0;
_tabBtnGenerate.style.borderTopRightRadius = 0;
_tabBtnGenerate.style.borderBottomLeftRadius = 0;
_tabBtnGenerate.style.borderBottomRightRadius = 0;
_tabBtnGenerate.style.color = new Color(0.9f, 0.9f, 0.9f);
_tabBtnGenerate.style.fontSize = 14;
_tabBtnGenerate.style.paddingLeft = 8;
_tabBtnGenerate.style.paddingRight = 8;
_tabBtnGenerate.style.paddingTop = 8;
_tabBtnGenerate.style.paddingBottom = 8;
_tabBtnGenerate.style.marginRight = 16;
_tabBtnSettings = new Button() { text = "系统设置" };
_tabBtnSettings.style.backgroundColor = Color.clear;
_tabBtnSettings.style.borderLeftWidth = 0;
_tabBtnSettings.style.borderRightWidth = 0;
_tabBtnSettings.style.borderTopWidth = 0;
_tabBtnSettings.style.borderBottomWidth = 2;
_tabBtnSettings.style.borderBottomColor = Color.clear;
_tabBtnSettings.style.borderTopLeftRadius = 0;
_tabBtnSettings.style.borderTopRightRadius = 0;
_tabBtnSettings.style.borderBottomLeftRadius = 0;
_tabBtnSettings.style.borderBottomRightRadius = 0;
_tabBtnSettings.style.color = new Color(0.7f, 0.7f, 0.7f);
_tabBtnSettings.style.fontSize = 14;
_tabBtnSettings.style.paddingLeft = 8;
_tabBtnSettings.style.paddingRight = 8;
_tabBtnSettings.style.paddingTop = 8;
_tabBtnSettings.style.paddingBottom = 8;
_tabBtnSettings.style.marginRight = 16;
_tabBtnFeedback = new Button() { text = "反馈意见" };
_tabBtnFeedback.style.backgroundColor = Color.clear;
_tabBtnFeedback.style.borderLeftWidth = 0;
_tabBtnFeedback.style.borderRightWidth = 0;
_tabBtnFeedback.style.borderTopWidth = 0;
_tabBtnFeedback.style.borderBottomWidth = 2;
_tabBtnFeedback.style.borderBottomColor = Color.clear;
_tabBtnFeedback.style.borderTopLeftRadius = 0;
_tabBtnFeedback.style.borderTopRightRadius = 0;
_tabBtnFeedback.style.borderBottomLeftRadius = 0;
_tabBtnFeedback.style.borderBottomRightRadius = 0;
_tabBtnFeedback.style.color = new Color(0.7f, 0.7f, 0.7f);
_tabBtnFeedback.style.fontSize = 14;
_tabBtnFeedback.style.paddingLeft = 8;
_tabBtnFeedback.style.paddingRight = 8;
_tabBtnFeedback.style.paddingTop = 8;
_tabBtnFeedback.style.paddingBottom = 8;
tabNav.Add(_tabBtnGenerate);
tabNav.Add(_tabBtnSettings);
tabNav.Add(_tabBtnFeedback);
_mainPanel.Add(tabNav);
// ===== Tab内容容器 =====
var tabContentContainer = new VisualElement()
{
style = { flexGrow = 1 }
};
// ===== 生成界面Tab =====
_tabGenerate = new VisualElement()
{
style = { flexGrow = 1 }
};
// ===== 效果图区域(拖放区域 - 支持选图前后切换)=====
_imageDropZone = new VisualElement()
{
style =
{
marginTop = 5,
height = 120,
borderLeftWidth = 1,
borderRightWidth = 1,
borderTopWidth = 1,
borderBottomWidth = 1,
borderLeftColor = new Color(0.35f, 0.35f, 0.38f),
borderRightColor = new Color(0.35f, 0.35f, 0.38f),
borderTopColor = new Color(0.35f, 0.35f, 0.38f),
borderBottomColor = new Color(0.35f, 0.35f, 0.38f),
borderTopLeftRadius = 8,
borderTopRightRadius = 8,
borderBottomLeftRadius = 8,
borderBottomRightRadius = 8,
flexDirection = FlexDirection.Row,
alignItems = Align.Center,
backgroundColor = new Color(0.14f, 0.14f, 0.16f),
overflow = Overflow.Hidden
}
};
_imagePathField = new TextField() { style = { display = DisplayStyle.None } };
_tabGenerate.Add(_imagePathField);
// 图片预览(左侧)
_dropZoneImage = new Image()
{
scaleMode = ScaleMode.ScaleToFit,
style =
{
width = 76,
height = 76,
marginLeft = 20,
marginTop = 2,
marginBottom = 2,
borderTopLeftRadius = 6,
borderTopRightRadius = 6,
borderBottomLeftRadius = 6,
borderBottomRightRadius = 6,
display = DisplayStyle.None // 默认隐藏
}
};
_imageDropZone.Add(_dropZoneImage);
// 文字和信息区域(右侧)
var textContainer = new VisualElement()
{
style =
{
flexGrow = 1,
alignItems = Align.Center,
justifyContent = Justify.Center,
paddingLeft = 10,
paddingRight = 10
}
};
_dropZoneText = new Label("点击选择界面效果图文件")
{
style =
{
color = new Color(0.95f, 0.95f, 0.95f),
fontSize = 14,
unityTextAlign = TextAnchor.MiddleCenter
}
};
textContainer.Add(_dropZoneText);
_dropZoneInfo = new Label("")
{
style =
{
color = new Color(0.7f, 0.7f, 0.7f),
fontSize = 11,
marginTop = 4,
display = DisplayStyle.None // 默认隐藏
}
};
textContainer.Add(_dropZoneInfo);
_imageDropZone.Add(textContainer);
// 清除按钮(右上角,默认隐藏)
var clearImageBtn = new Button() { text = "✕" };
clearImageBtn.style.position = Position.Absolute;
clearImageBtn.style.right = 4;
clearImageBtn.style.top = 4;
clearImageBtn.style.width = 20;
clearImageBtn.style.height = 20;
clearImageBtn.style.fontSize = 12;
clearImageBtn.style.backgroundColor = new Color(0.2f, 0.2f, 0.22f, 0.8f);
clearImageBtn.style.borderLeftWidth = 0;
clearImageBtn.style.borderRightWidth = 0;
clearImageBtn.style.borderTopWidth = 0;
clearImageBtn.style.borderBottomWidth = 0;
clearImageBtn.style.borderTopLeftRadius = 4;
clearImageBtn.style.borderTopRightRadius = 4;
clearImageBtn.style.borderBottomLeftRadius = 4;
clearImageBtn.style.borderBottomRightRadius = 4;
clearImageBtn.style.color = new Color(0.9f, 0.9f, 0.9f);
clearImageBtn.style.display = DisplayStyle.None; // 默认隐藏
clearImageBtn.name = "clearImageBtn";
clearImageBtn.clicked += ClearImagePreview;
_imageDropZone.Add(clearImageBtn);
// 点击选择图片
_imageDropZone.RegisterCallback(evt =>
{
// 如果点击的是清除按钮,不触发选择
if (evt.target is Button) return;
OnBrowseImageClicked();
});
_tabGenerate.Add(_imageDropZone);
// ===== 切图文件夹区域(拖放区域 - 支持选文件夹前后切换)=====
_textureDropZone = new VisualElement()
{
style =
{
marginTop = 15,
height = 80,
borderLeftWidth = 1,
borderRightWidth = 1,
borderTopWidth = 1,
borderBottomWidth = 1,
borderLeftColor = new Color(0.35f, 0.35f, 0.38f),
borderRightColor = new Color(0.35f, 0.35f, 0.38f),
borderTopColor = new Color(0.35f, 0.35f, 0.38f),
borderBottomColor = new Color(0.35f, 0.35f, 0.38f),
borderTopLeftRadius = 8,
borderTopRightRadius = 8,
borderBottomLeftRadius = 8,
borderBottomRightRadius = 8,
flexDirection = FlexDirection.Column,
justifyContent = Justify.Center,
alignItems = Align.Center,
backgroundColor = new Color(0.14f, 0.14f, 0.16f),
position = Position.Relative
}
};
_texturePathField = new TextField() { style = { display = DisplayStyle.None } };
_tabGenerate.Add(_texturePathField);
// 文件夹图标和名称
var textureHeaderRow = new VisualElement()
{
style = { flexDirection = FlexDirection.Row, alignItems = Align.Center }
};
_textureDropText = new Label("点击选择切图所在文件夹(可选)")
{
style = { color = new Color(0.95f, 0.95f, 0.95f), fontSize = 14 }
};
textureHeaderRow.Add(_textureDropText);
_textureDropZone.Add(textureHeaderRow);
// 路径显示(选中后显示)
_textureDropPath = new Label("")
{
style =
{
color = new Color(0.5f, 0.5f, 0.5f),
fontSize = 10,
marginTop = 4,
unityTextAlign = TextAnchor.MiddleCenter,
display = DisplayStyle.None
}
};
_textureDropZone.Add(_textureDropPath);
// 文件数量(选中后显示)
_textureDropCount = new Label("")
{
style =
{
color = new Color(0.2f, 0.7f, 0.3f),
fontSize = 11,
marginTop = 2,
display = DisplayStyle.None
}
};
_textureDropZone.Add(_textureDropCount);
// 清除按钮(右上角,默认隐藏)
var clearTextureBtn = new Button() { text = "✕" };
clearTextureBtn.style.position = Position.Absolute;
clearTextureBtn.style.right = 4;
clearTextureBtn.style.top = 4;
clearTextureBtn.style.width = 20;
clearTextureBtn.style.height = 20;
clearTextureBtn.style.fontSize = 12;
clearTextureBtn.style.backgroundColor = new Color(0.2f, 0.2f, 0.22f, 0.8f);
clearTextureBtn.style.borderLeftWidth = 0;
clearTextureBtn.style.borderRightWidth = 0;
clearTextureBtn.style.borderTopWidth = 0;
clearTextureBtn.style.borderBottomWidth = 0;
clearTextureBtn.style.borderTopLeftRadius = 4;
clearTextureBtn.style.borderTopRightRadius = 4;
clearTextureBtn.style.borderBottomLeftRadius = 4;
clearTextureBtn.style.borderBottomRightRadius = 4;
clearTextureBtn.style.color = new Color(0.9f, 0.9f, 0.9f);
clearTextureBtn.style.display = DisplayStyle.None;
clearTextureBtn.name = "clearTextureBtn";
clearTextureBtn.clicked += ClearTextureFolder;
_textureDropZone.Add(clearTextureBtn);
// 点击选择文件夹
_textureDropZone.RegisterCallback(evt =>
{
if (evt.target is Button) return;
OnBrowseTextureClicked();
});
_tabGenerate.Add(_textureDropZone);
// ===== 生成按钮(圆角样式)=====
_generateBtn = new Button()
{
text = "生成界面",
style = {
height = 44,
marginTop = 20,
backgroundColor = new Color(0.25f, 0.55f, 0.95f),
color = Color.white,
fontSize = 14,
unityFontStyleAndWeight = FontStyle.Bold,
borderTopLeftRadius = 8,
borderTopRightRadius = 8,
borderBottomLeftRadius = 8,
borderBottomRightRadius = 8
}
};
_tabGenerate.Add(_generateBtn);
// ===== 状态栏 =====
_statusBarContainer = new VisualElement()
{
style =
{
flexDirection = FlexDirection.Row,
alignItems = Align.Center,
marginTop = 15,
paddingTop = 8,
paddingBottom = 4
}
};
_statusDot = new VisualElement()
{
style =
{
width = 6,
height = 6,
borderTopLeftRadius = 3,
borderTopRightRadius = 3,
borderBottomLeftRadius = 3,
borderBottomRightRadius = 3,
backgroundColor = new Color(0.4f, 0.6f, 1f), // 默认蓝色(加载中)
marginRight = 6
}
};
_statusBarContainer.Add(_statusDot);
_statusLabel = new Label("正在初始化...") { style = { flexGrow = 1, color = new Color(0.9f, 0.9f, 0.9f), fontSize = 13 } };
_statusBarContainer.Add(_statusLabel);
_feedbackErrorBtn = new Button() { text = "反馈问题" };
_feedbackErrorBtn.style.display = DisplayStyle.None; // 默认隐藏
_feedbackErrorBtn.style.fontSize = 11;
_feedbackErrorBtn.style.backgroundColor = Color.clear;
_feedbackErrorBtn.style.borderLeftWidth = 0;
_feedbackErrorBtn.style.borderRightWidth = 0;
_feedbackErrorBtn.style.borderTopWidth = 0;
_feedbackErrorBtn.style.borderBottomWidth = 0;
_feedbackErrorBtn.style.color = new Color(0.4f, 0.6f, 1f);
_feedbackErrorBtn.style.paddingTop = 0;
_feedbackErrorBtn.style.paddingBottom = 0;
_feedbackErrorBtn.style.paddingLeft = 8;
_feedbackErrorBtn.style.paddingRight = 8;
_feedbackErrorBtn.clicked += OnFeedbackErrorClicked;
_statusBarContainer.Add(_feedbackErrorBtn);
_tabGenerate.Add(_statusBarContainer);
// ===== 生成结果区域 - 仅DEBUG模式显示,放到最下面 =====
if (DEBUG_SHOW_RESULT)
{
_tabGenerate.Add(new Label("生成结果") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginTop = 15 } });
// 视图切换工具栏
var viewToolbar = new VisualElement() { style = { flexDirection = FlexDirection.Row, marginBottom = 5 } };
_viewModeMenu = new ToolbarMenu();
_viewModeMenu.text = "JSON视图";
_viewModeMenu.menu.AppendAction("JSON视图", a => SwitchViewMode(ViewMode.Json), a => _currentViewMode == ViewMode.Json ? DropdownMenuAction.Status.Checked : DropdownMenuAction.Status.Normal);
_viewModeMenu.menu.AppendAction("树形视图", a => SwitchViewMode(ViewMode.Tree), a => _currentViewMode == ViewMode.Tree ? DropdownMenuAction.Status.Checked : DropdownMenuAction.Status.Normal);
viewToolbar.Add(_viewModeMenu);
viewToolbar.Add(new VisualElement() { style = { flexGrow = 1 } });
var expandAllBtn = new Button() { text = "全部展开" };
expandAllBtn.clicked += ExpandAllTreeNodes;
viewToolbar.Add(expandAllBtn);
var collapseAllBtn = new Button() { text = "全部折叠", style = { marginLeft = 5 } };
collapseAllBtn.clicked += CollapseAllTreeNodes;
viewToolbar.Add(collapseAllBtn);
var copyBtn = new Button() { text = "复制", style = { marginLeft = 5 } };
copyBtn.clicked += CopyResultToClipboard;
viewToolbar.Add(copyBtn);
_tabGenerate.Add(viewToolbar);
// 结果视图容器
_resultViewContainer = new VisualElement()
{
style =
{
height = 200,
display = DisplayStyle.Flex, // 默认显示,方便测试时输入JSON
borderTopWidth = 1,
borderBottomWidth = 1,
borderLeftWidth = 1,
borderRightWidth = 1,
borderTopColor = new Color(0.3f, 0.3f, 0.3f),
borderBottomColor = new Color(0.3f, 0.3f, 0.3f),
borderLeftColor = new Color(0.3f, 0.3f, 0.3f),
borderRightColor = new Color(0.3f, 0.3f, 0.3f),
borderTopLeftRadius = 4,
borderTopRightRadius = 4,
borderBottomLeftRadius = 4,
borderBottomRightRadius = 4
}
};
// JSON视图 - 包装在ScrollView中以支持滚动
_jsonScrollView = new ScrollView()
{
style =
{
flexGrow = 1,
display = DisplayStyle.Flex
}
};
_jsonViewField = new TextField()
{
multiline = true,
style =
{
minWidth = 300, // 设置最小宽度确保内容不会被压缩
unityFont = Resources.Load("Fonts/Consolas")
}
};
// 【修复】TextField 可编辑,方便测试时手动输入JSON
_jsonScrollView.Add(_jsonViewField);
// 树形视图
_treeViewContainer = new ScrollView()
{
style =
{
flexGrow = 1,
display = DisplayStyle.None // 默认隐藏
}
};
_resultViewContainer.Add(_jsonScrollView);
_resultViewContainer.Add(_treeViewContainer);
_tabGenerate.Add(_resultViewContainer);
}
// 状态栏不在 _tabGenerate 内,而是在根级别
tabContentContainer.Add(_tabGenerate);
// ===== 设置Tab =====
_tabSettings = new VisualElement()
{
style = { flexGrow = 1, display = DisplayStyle.None } // 默认隐藏
};
// ===== 公共切图文件夹卡片 =====
var settingsCard = new VisualElement()
{
style =
{
backgroundColor = new Color(0.14f, 0.14f, 0.16f),
borderLeftWidth = 1,
borderRightWidth = 1,
borderTopWidth = 1,
borderBottomWidth = 1,
borderLeftColor = new Color(0.35f, 0.35f, 0.38f),
borderRightColor = new Color(0.35f, 0.35f, 0.38f),
borderTopColor = new Color(0.35f, 0.35f, 0.38f),
borderBottomColor = new Color(0.35f, 0.35f, 0.38f),
borderTopLeftRadius = 8,
borderTopRightRadius = 8,
borderBottomLeftRadius = 8,
borderBottomRightRadius = 8,
paddingTop = 14,
paddingBottom = 14,
paddingLeft = 14,
paddingRight = 14
}
};
// 标题行(图标+标题+数量徽章)
var settingsHeaderRow = new VisualElement()
{
style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, marginBottom = 12 }
};
var settingsTitle = new Label("公共切图文件夹")
{
style = { color = new Color(0.95f, 0.95f, 0.95f), fontSize = 14, unityFontStyleAndWeight = FontStyle.Bold }
};
settingsHeaderRow.Add(settingsTitle);
// 数量徽章
_folderCountBadge = new Label("0/5")
{
style =
{
backgroundColor = new Color(0.25f, 0.55f, 0.95f),
color = Color.white,
paddingLeft = 8,
paddingRight = 8,
paddingTop = 2,
paddingBottom = 2,
borderTopLeftRadius = 10,
borderTopRightRadius = 10,
borderBottomLeftRadius = 10,
borderBottomRightRadius = 10,
fontSize = 11,
marginLeft = 8
}
};
settingsHeaderRow.Add(_folderCountBadge);
settingsCard.Add(settingsHeaderRow);
// 公共文件夹容器
_commonFoldersContainer = new VisualElement()
{
style =
{
marginBottom = 12,
minHeight = 60
}
};
settingsCard.Add(_commonFoldersContainer);
// 添加文件夹按钮
_addCommonFolderBtn = new Button() { text = "+ 添加文件夹" };
_addCommonFolderBtn.style.height = 36;
_addCommonFolderBtn.style.fontSize = 13;
_addCommonFolderBtn.style.borderTopLeftRadius = 6;
_addCommonFolderBtn.style.borderTopRightRadius = 6;
_addCommonFolderBtn.style.borderBottomLeftRadius = 6;
_addCommonFolderBtn.style.borderBottomRightRadius = 6;
_addCommonFolderBtn.style.backgroundColor = new Color(0.25f, 0.55f, 0.95f);
_addCommonFolderBtn.style.color = Color.white;
settingsCard.Add(_addCommonFolderBtn);
// 提示文字
_commonFoldersHint = new Label("公共切图文件夹只需设置一次,下次自动加载。")
{
style =
{
color = new Color(0.6f, 0.6f, 0.65f),
fontSize = 11,
marginTop = 12,
whiteSpace = WhiteSpace.Normal
}
};
settingsCard.Add(_commonFoldersHint);
_tabSettings.Add(settingsCard);
tabContentContainer.Add(_tabSettings);
// ===== 反馈Tab =====
_tabFeedback = new VisualElement()
{
style = { flexGrow = 1, display = DisplayStyle.None } // 默认隐藏
};
_tabFeedback.Add(new Label("有问题或建议?请告诉我们:")
{
style = {
color = new Color(0.85f, 0.85f, 0.85f),
fontSize = 13,
marginTop = 5,
marginBottom = 12
}
});
_feedbackTextField = new TextField()
{
multiline = true,
value = "", // 设置初始值,避免 null
tooltip = "请描述您遇到的问题或建议...",
style =
{
height = 150,
marginBottom = 12,
borderTopLeftRadius = 6,
borderTopRightRadius = 6,
borderBottomLeftRadius = 6,
borderBottomRightRadius = 6,
backgroundColor = new Color(0.15f, 0.15f, 0.17f)
}
};
_tabFeedback.Add(_feedbackTextField);
var feedbackSubmitRow = new VisualElement()
{
style = { flexDirection = FlexDirection.Row, alignItems = Align.Center }
};
_feedbackHintLabel = new Label("")
{
style =
{
flexGrow = 1,
color = new Color(0.9f, 0.3f, 0.3f),
fontSize = 12
}
};
feedbackSubmitRow.Add(_feedbackHintLabel);
_feedbackSubmitBtn = new Button() { text = "提交" };
_feedbackSubmitBtn.style.width = 100;
_feedbackSubmitBtn.style.height = 36;
_feedbackSubmitBtn.style.fontSize = 14;
_feedbackSubmitBtn.style.borderTopLeftRadius = 6;
_feedbackSubmitBtn.style.borderTopRightRadius = 6;
_feedbackSubmitBtn.style.borderBottomLeftRadius = 6;
_feedbackSubmitBtn.style.borderBottomRightRadius = 6;
_feedbackSubmitBtn.style.backgroundColor = new Color(0.25f, 0.55f, 0.95f);
_feedbackSubmitBtn.style.color = Color.white;
_feedbackSubmitBtn.clicked += OnFeedbackSubmitClicked;
feedbackSubmitRow.Add(_feedbackSubmitBtn);
_tabFeedback.Add(feedbackSubmitRow);
// 反馈成功提示
_feedbackSuccessContainer = new VisualElement()
{
style =
{
display = DisplayStyle.None,
flexDirection = FlexDirection.Row,
alignItems = Align.Center,
justifyContent = Justify.Center,
marginTop = 30,
paddingTop = 20,
paddingBottom = 20,
backgroundColor = new Color(0.15f, 0.3f, 0.15f),
borderTopLeftRadius = 8,
borderTopRightRadius = 8,
borderBottomLeftRadius = 8,
borderBottomRightRadius = 8
}
};
var successIcon = new Label("✓")
{
style =
{
fontSize = 32,
color = new Color(0.2f, 0.9f, 0.2f),
marginRight = 15
}
};
_feedbackSuccessContainer.Add(successIcon);
var successText = new Label("感谢您的反馈!")
{
style =
{
fontSize = 16,
unityFontStyleAndWeight = FontStyle.Bold,
color = new Color(0.2f, 0.9f, 0.2f)
}
};
_feedbackSuccessContainer.Add(successText);
_tabFeedback.Add(_feedbackSuccessContainer);
tabContentContainer.Add(_tabFeedback);
_mainPanel.Add(tabContentContainer);
root.Add(_mainPanel);
// 【修复】默认面板显示逻辑:
// 1. 认证面板默认隐藏,只有用户点击登录相关操作时才显示
// 2. 主面板默认显示,用户可以直接看到功能界面
_authPanel.style.display = DisplayStyle.None;
_mainPanel.style.display = DisplayStyle.Flex;
// ===== 加载动画遮罩层 =====
CreateLoadingOverlay(root);
}
///
/// 创建加载动画遮罩层
///
private void CreateLoadingOverlay(VisualElement root)
{
_loadingOverlay = new VisualElement()
{
name = "LoadingOverlay",
pickingMode = PickingMode.Position,
style =
{
position = Position.Absolute,
left = 0, right = 0, top = 0, bottom = 0,
backgroundColor = new Color(0.08f, 0.08f, 0.1f, 0.95f),
justifyContent = Justify.Center,
alignItems = Align.Center,
flexDirection = FlexDirection.Column,
display = DisplayStyle.None
}
};
// 内容容器 - 固定宽度
var contentContainer = new VisualElement()
{
style =
{
width = 220,
height = 80,
alignItems = Align.Center,
flexDirection = FlexDirection.Column
}
};
// 进度条背景 - 固定宽度
var progressBg = new VisualElement()
{
style =
{
width = 180,
height = 4,
backgroundColor = new Color(0.2f, 0.2f, 0.22f),
borderTopLeftRadius = 2,
borderTopRightRadius = 2,
borderBottomLeftRadius = 2,
borderBottomRightRadius = 2,
marginBottom = 20,
overflow = Overflow.Hidden
}
};
// 进度条填充(动画条)- 固定宽度
_loadingSpinner = new VisualElement()
{
name = "LoadingBar",
style =
{
width = 60,
height = 4,
backgroundColor = new Color(0.25f, 0.55f, 0.95f),
borderTopLeftRadius = 2,
borderTopRightRadius = 2,
borderBottomLeftRadius = 2,
borderBottomRightRadius = 2
}
};
progressBg.Add(_loadingSpinner);
contentContainer.Add(progressBg);
// 加载提示文字 - 固定宽度,左对齐文字但整体居中
_loadingText = new Label("正在分析设计图...")
{
style =
{
width = 180,
color = new Color(0.95f, 0.95f, 0.95f),
fontSize = 14,
unityTextAlign = TextAnchor.MiddleCenter
}
};
contentContainer.Add(_loadingText);
// 时间提示 - 固定宽度
var timeHint = new Label("分析可能需要 1-3 分钟")
{
style =
{
width = 180,
color = new Color(0.5f, 0.5f, 0.55f),
fontSize = 11,
marginTop = 8,
unityTextAlign = TextAnchor.MiddleCenter
}
};
contentContainer.Add(timeHint);
_loadingOverlay.Add(contentContainer);
root.Add(_loadingOverlay);
}
///
/// 显示加载动画
///
private void ShowLoadingOverlay(string message = "正在分析设计图...")
{
if (_loadingOverlay == null) return;
_loadingText.text = message;
_loadingOverlay.style.display = DisplayStyle.Flex;
_loadingStartTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
_loadingDots = 0;
_spinnerRotation = 0f;
// 强制重绘
_loadingOverlay.MarkDirtyRepaint();
// 启动动画 - 使用 EditorApplication.update
EditorApplication.update -= UpdateLoadingAnimation;
EditorApplication.update += UpdateLoadingAnimation;
}
///
/// 隐藏加载动画
///
private void HideLoadingOverlay()
{
if (_loadingOverlay == null) return;
_loadingOverlay.style.display = DisplayStyle.None;
// 停止动画
EditorApplication.update -= UpdateLoadingAnimation;
}
///
/// 更新加载动画(进度条来回移动)
///
private void UpdateLoadingAnimation()
{
if (_loadingOverlay == null || _loadingOverlay.style.display == DisplayStyle.None)
return;
_spinnerRotation += 1f; // 再降低速度
// 进度条来回移动动画
float progress = Mathf.PingPong(_spinnerRotation / 2000f, 1f);
float barWidth = 60f;
float containerWidth = 180f;
float maxMargin = containerWidth - barWidth;
float marginLeft = progress * maxMargin;
if (_loadingSpinner != null)
{
_loadingSpinner.style.marginLeft = marginLeft;
}
// 动态点点点(每500ms更新一次)
int dotCount = ((int)(_spinnerRotation / 600f)) % 4;
string dots = new string('.', dotCount);
string spaces = new string(' ', 3 - dotCount);
// 更新耗时显示 - 始终显示时间格式,固定宽度
if (_loadingStartTime > 0)
{
long elapsed = (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - _loadingStartTime) / 1000;
// 始终显示时间,保持格式一致
_loadingText.text = $"正在分析设计图{dots}{spaces} ({elapsed,2}秒)";
}
}
private void BindUIElements()
{
// 主界面按钮
if (_browseImageBtn != null)
_browseImageBtn.clicked += OnBrowseImageClicked;
if (_browseTextureBtn != null)
_browseTextureBtn.clicked += OnBrowseTextureClicked;
if (_generateBtn != null)
_generateBtn.clicked += OnGenerateClicked;
// 登录/注册切换
if (_loginBtn != null)
_loginBtn.clicked += OnLoginClicked;
if (_showRegisterBtn != null)
_showRegisterBtn.clicked += OnShowRegisterClicked;
if (_showLoginBtn != null)
_showLoginBtn.clicked += OnShowLoginClicked;
if (_registerBtn != null)
_registerBtn.clicked += OnRegisterClicked;
if (_sendCodeBtn != null)
_sendCodeBtn.clicked += OnSendCodeClicked;
// 【新增】重置密码相关事件
if (_forgotPasswordBtn != null)
_forgotPasswordBtn.clicked += OnForgotPasswordClicked;
if (_resetSendCodeBtn != null)
_resetSendCodeBtn.clicked += OnResetSendCodeClicked;
if (_resetPasswordBtn != null)
_resetPasswordBtn.clicked += OnResetPasswordClicked;
if (_resetBackToLoginBtn != null)
_resetBackToLoginBtn.clicked += OnShowLoginClicked;
// Tab切换
if (_tabBtnGenerate != null)
_tabBtnGenerate.clicked += () => SwitchTab(TabType.Generate);
if (_tabBtnSettings != null)
_tabBtnSettings.clicked += () => SwitchTab(TabType.Settings);
if (_tabBtnFeedback != null)
_tabBtnFeedback.clicked += () => SwitchTab(TabType.Feedback);
// 设置Tab - 添加公共文件夹按钮
if (_addCommonFolderBtn != null)
_addCommonFolderBtn.clicked += OnAddCommonFolderClicked;
}
///
/// 检查更新(自动)
///
private async Task CheckForUpdatesAsync()
{
try
{
if (_updateService == null) return;
Log("检查更新中...");
var updateInfo = await _updateService.CheckUpdateAsync();
if (updateInfo != null)
{
Log($"发现新版本: {updateInfo.version}");
// 在主线程显示更新提示
EditorApplication.delayCall += () =>
{
bool confirm;
if (updateInfo.mandatory)
{
// 强制更新:不提供取消选项
confirm = EditorUtility.DisplayDialog(
"发现新版本(必须更新)",
$"当前版本: {Config.Version}\n新版本: {updateInfo.version}\n\n{updateInfo.releaseNotes}\n\n⚠️ 此更新为强制更新,必须安装后才能继续使用。",
"立即更新",
""
);
}
else
{
// 可选更新:提供稍后选项
confirm = EditorUtility.DisplayDialog(
"发现新版本",
$"当前版本: {Config.Version}\n新版本: {updateInfo.version}\n\n{updateInfo.releaseNotes}",
"立即更新",
"稍后"
);
}
if (confirm)
{
_ = DownloadAndInstallUpdateAsync(updateInfo);
}
};
}
else
{
Log("当前已是最新版本");
}
}
catch (Exception ex)
{
Log($"检查更新失败: {ex.Message}");
}
}
///
/// 下载并安装更新
///
private async Task DownloadAndInstallUpdateAsync(UpdateInfo updateInfo)
{
try
{
Log("下载更新中...");
bool success = await _updateService.DownloadAndInstallAsync(updateInfo);
if (success)
{
Log("更新安装成功");
// 刷新 Unity 资产数据库
EditorApplication.delayCall += () =>
{
AssetDatabase.Refresh();
EditorUtility.DisplayDialog(
"更新完成",
$"已成功更新到版本 {updateInfo.version}\n\n请重启 Unity 编辑器以应用更新。",
"确定"
);
};
}
else
{
LogError("更新安装失败,请查看 Console 了解详细错误信息");
EditorUtility.DisplayDialog(
"更新失败",
"更新安装失败。\n\n请查看 Console (Window > General > Console) 了解详细信息。",
"确定"
);
}
}
catch (Exception ex)
{
LogError($"更新失败: {ex.Message}");
EditorUtility.DisplayDialog("更新错误", $"更新过程中发生错误:\n\n{ex.Message}", "确定");
}
}
///
/// 刷新授权状态(支持强制刷新)
/// 从服务端获取最新的授权和额度信息
///
private async Task RefreshAuthStateAsync(bool forceRefresh = false)
{
if (_authService == null)
{
LogError("授权服务未初始化");
return;
}
try
{
Log(forceRefresh ? "正在从服务器获取最新授权状态..." : "正在读取授权状态...");
_currentAuthState = await Task.Run(() => _authService.GetAuthSync(_authService.Token));
// 更新UI显示(包含额度更新)
EditorApplication.delayCall += () =>
{
UpdateUIBasedOnState();
};
// 记录状态日志 - 【修复】使用 UserState 判断
var userState = GetUserState();
switch (userState)
{
case UserState.LoggedIn:
Log($"授权状态: 已登录 | 套餐: {GetPlanDisplayName(_currentAuthState.Plan)} | 额度: {_currentAuthState.Credits}/{_currentAuthState.MaxCredits}");
break;
case UserState.Trial:
Log($"授权状态: 试用模式 | 剩余 {_currentAuthState.RemainingTrials} 次");
break;
case UserState.TrialExhausted:
Log($"授权状态: 试用耗尽 | {_currentAuthState.Message}");
break;
}
}
catch (Exception ex)
{
LogError($"刷新授权状态失败: {ex.Message}");
// 出错时回退到本地检查
try
{
_currentAuthState = await Task.Run(() => _authService.GetAuthSync(_authService.Token));
EditorApplication.delayCall += () =>
{
UpdateUIBasedOnState();
};
}
catch
{
// 完全失败,显示登录界面
// 【修复】试用耗尽时 Type 仍是 Trial,通过 RemainingTrials=0 表示
_currentAuthState = new AuthResult { Type = AuthType.Trial, Valid = false, RemainingTrials = 0 };
EditorApplication.delayCall += () =>
{
_authPanel.style.display = DisplayStyle.Flex;
_mainPanel.style.display = DisplayStyle.None;
ShowLoginPanel();
};
}
}
}
///
/// 【关键修复】更新认证面板可见性 - 使用 UserState 判断
///
private void UpdateAuthPanelVisibility()
{
if (_authPanel == null || _mainPanel == null) return;
var userState = GetUserState();
switch (userState)
{
case UserState.LoggedIn:
// 已登录:显示主面板
_authPanel.style.display = DisplayStyle.None;
_mainPanel.style.display = DisplayStyle.Flex;
break;
case UserState.Trial:
// 试用中:显示主面板
_authPanel.style.display = DisplayStyle.None;
_mainPanel.style.display = DisplayStyle.Flex;
break;
case UserState.TrialExhausted:
// 试用耗尽:显示主面板
_authPanel.style.display = DisplayStyle.None;
_mainPanel.style.display = DisplayStyle.Flex;
break;
default:
// 未授权:显示认证面板
_authPanel.style.display = DisplayStyle.Flex;
_mainPanel.style.display = DisplayStyle.None;
ShowLoginPanel();
break;
}
}
///
/// 获取套餐显示名称
///
private string GetPlanDisplayName(string plan)
{
return plan?.ToLower() switch
{
"trial" => "试用版",
"free" => "免费版",
"starter" => "畅享版",
"professional" => "专业版",
"enterprise" => "企业版",
_ => "免费版"
};
}
///
/// 根据用户状态更新UI - 对应 plugin 的 updateUIBasedOnState()
///
private void UpdateUIBasedOnState()
{
if (_currentAuthState == null || _generateBtn == null) return;
var userState = GetUserState();
Log($"当前用户状态: {userState}");
// 先更新面板可见性
UpdateAuthPanelVisibility();
switch (userState)
{
case UserState.Trial:
// 试用中:隐藏授权工具栏,显示正常生成按钮
if (_authToolbar != null) _authToolbar.style.display = DisplayStyle.None;
_generateBtn.text = "生成界面";
_generateBtn.SetEnabled(true);
// 清除旧的事件处理
_generateBtn.clicked -= OnGenerateClicked;
_generateBtn.clicked -= ShowLoginPanel;
_generateBtn.clicked -= ShowSubscribePanel;
_generateBtn.clicked += OnGenerateClicked;
break;
case UserState.TrialExhausted:
// 试用耗尽:隐藏授权工具栏,显示登录引导按钮
if (_authToolbar != null) _authToolbar.style.display = DisplayStyle.None;
_generateBtn.text = "试用次数已用完,登录获取更多";
_generateBtn.SetEnabled(true);
_generateBtn.clicked -= OnGenerateClicked;
_generateBtn.clicked -= ShowLoginPanel;
_generateBtn.clicked -= ShowSubscribePanel;
_generateBtn.clicked += ShowLoginPanel;
break;
case UserState.LoggedIn:
// 已登录:显示授权工具栏并更新内容
UpdateAuthToolbar();
// 检查是否有额度
bool hasQuota = _currentAuthState.Credits > 0 || _currentAuthState.MaxCredits == -1 || _currentAuthState.MaxCredits >= 999999;
if (hasQuota)
{
_generateBtn.text = "生成界面";
_generateBtn.SetEnabled(true);
_generateBtn.clicked -= OnGenerateClicked;
_generateBtn.clicked -= ShowLoginPanel;
_generateBtn.clicked -= ShowSubscribePanel;
_generateBtn.clicked += OnGenerateClicked;
}
else
{
_generateBtn.text = "额度已用完,订阅解锁更多";
_generateBtn.SetEnabled(true);
_generateBtn.clicked -= OnGenerateClicked;
_generateBtn.clicked -= ShowLoginPanel;
_generateBtn.clicked -= ShowSubscribePanel;
_generateBtn.clicked += ShowSubscribePanel;
}
break;
}
}
///
/// 【关键修复】获取当前用户状态 - 对齐 Plugin 的 getUserState() 逻辑
/// 用户状态由 AuthType 和 RemainingTrials 计算得出,不是直接存储的
///
private UserState GetUserState()
{
if (_currentAuthState == null) return UserState.Trial;
// 已登录(非试用)- Valid 为 true 且 Type 不是 Trial
if (_currentAuthState.Valid && _currentAuthState.Type != AuthType.Trial)
{
return UserState.LoggedIn;
}
// 试用耗尽 - Type 是 Trial,但剩余次数 <= 0 或 Valid 为 false
if (_currentAuthState.Type == AuthType.Trial &&
(_currentAuthState.RemainingTrials <= 0 || !_currentAuthState.Valid))
{
return UserState.TrialExhausted;
}
// 试用中
return UserState.Trial;
}
///
/// 【修复】更新授权工具栏显示 - 使用 UserState 判断
///
private void UpdateAuthToolbar()
{
if (_authToolbar == null || _currentAuthState == null) return;
var userState = GetUserState();
// 已登录(非试用)时显示工具栏
if (userState == UserState.LoggedIn)
{
_authToolbar.style.display = DisplayStyle.Flex;
// 更新徽章
if (_toolbarBadge != null)
{
_toolbarBadge.text = GetPlanDisplayName(_currentAuthState.Plan);
_toolbarBadge.AddToClassList($"badge-{_currentAuthState.Plan?.ToLower() ?? "free"}");
}
// 更新额度显示
if (_quotaText != null)
{
if (_currentAuthState.MaxCredits == -1 || _currentAuthState.MaxCredits >= 999999)
{
_quotaText.text = "无限";
}
else
{
_quotaText.text = $"{_currentAuthState.Credits}/{_currentAuthState.MaxCredits}";
// 额度低时变红色
if (_currentAuthState.Credits <= 3)
{
_quotaText.style.color = Color.red;
}
}
}
// 显示/隐藏升级按钮
if (_toolbarUpgradeBtn != null)
{
bool showUpgrade = _currentAuthState.Plan?.ToLower() is "trial" or "free" or "starter";
_toolbarUpgradeBtn.style.display = showUpgrade ? DisplayStyle.Flex : DisplayStyle.None;
}
}
else
{
_authToolbar.style.display = DisplayStyle.None;
}
}
///
/// 显示订阅面板
///
private void ShowSubscribePanel()
{
SubscribeWindow.ShowWindow();
}
private void ShowLoginPanel()
{
_authPanel.style.display = DisplayStyle.Flex;
_mainPanel.style.display = DisplayStyle.None;
_loginPanel.style.display = DisplayStyle.Flex;
_registerPanel.style.display = DisplayStyle.None;
_resetPasswordPanel.style.display = DisplayStyle.None;
ShowLoginError(""); // 清空错误提示
}
private void ShowRegisterPanel()
{
_loginPanel.style.display = DisplayStyle.None;
_registerPanel.style.display = DisplayStyle.Flex;
_resetPasswordPanel.style.display = DisplayStyle.None;
ShowRegisterError(""); // 清空错误提示
}
private void ShowResetPasswordPanel()
{
_loginPanel.style.display = DisplayStyle.None;
_registerPanel.style.display = DisplayStyle.None;
_resetPasswordPanel.style.display = DisplayStyle.Flex;
ShowResetError(""); // 清空错误提示
}
///
/// 显示登录错误提示
///
private void ShowLoginError(string message)
{
if (_loginErrorLabel == null) return;
if (string.IsNullOrEmpty(message))
{
_loginErrorLabel.style.display = DisplayStyle.None;
}
else
{
_loginErrorLabel.text = message;
_loginErrorLabel.style.display = DisplayStyle.Flex;
}
}
///
/// 显示注册错误提示
///
private void ShowRegisterError(string message)
{
if (_registerErrorLabel == null) return;
if (string.IsNullOrEmpty(message))
{
_registerErrorLabel.style.display = DisplayStyle.None;
}
else
{
_registerErrorLabel.text = message;
_registerErrorLabel.style.display = DisplayStyle.Flex;
}
}
///
/// 显示重置密码错误提示
///
private void ShowResetError(string message)
{
if (_resetErrorLabel == null) return;
if (string.IsNullOrEmpty(message))
{
_resetErrorLabel.style.display = DisplayStyle.None;
}
else
{
_resetErrorLabel.text = message;
_resetErrorLabel.style.display = DisplayStyle.Flex;
}
}
#region Event Handlers
private void OnBrowseImageClicked()
{
// 判断是否处于快速体验模式(效果图)
bool quickMode = EditorPrefs.GetBool(PREFS_QUICK_MODE_IMAGE, false);
string defaultPath;
if (quickMode)
{
// 快速体验模式:使用 Samples 路径
string samplePath = GetSampleImagesPath();
defaultPath = samplePath ?? "";
}
else
{
// 非快速体验模式:使用上次保存的路径,没有则为空
defaultPath = EditorPrefs.GetString(PREFS_LAST_IMAGE_DIR, "");
}
string path = EditorUtility.OpenFilePanelWithFilters(
"选择设计图",
defaultPath,
new[] { "图片文件", "png,jpg,jpeg,webp", "所有文件", "*" }
);
if (!string.IsNullOrEmpty(path))
{
_imagePathField.value = path;
ShowImagePreview(path);
// 保存用户选择的路径,清除快速体验模式
EditorPrefs.SetString(PREFS_LAST_IMAGE_DIR, Path.GetDirectoryName(path));
EditorPrefs.SetBool(PREFS_QUICK_MODE_IMAGE, false);
}
}
private void OnBrowseTextureClicked()
{
// 判断是否处于快速体验模式(切图文件夹)
bool quickMode = EditorPrefs.GetBool(PREFS_QUICK_MODE_TEXTURE, false);
string defaultPath;
if (quickMode)
{
// 快速体验模式:使用 Samples 路径
string samplePath = GetSampleSlicesPath();
defaultPath = samplePath ?? "";
}
else
{
// 非快速体验模式:使用上次保存的路径,没有则为空
defaultPath = EditorPrefs.GetString(PREFS_TEXTURE_FOLDER_PATH, "");
}
string path = EditorUtility.OpenFolderPanel("选择切图文件夹", defaultPath, "");
if (!string.IsNullOrEmpty(path))
{
_texturePathField.value = path;
// 保存路径,清除快速体验模式
EditorPrefs.SetString(PREFS_TEXTURE_FOLDER_PATH, path);
EditorPrefs.SetBool(PREFS_QUICK_MODE_TEXTURE, false);
// 自动扫描
OnScanTextureClicked();
}
}
private void OnScanTextureClicked()
{
string path = _texturePathField.value;
if (string.IsNullOrEmpty(path) || !Directory.Exists(path))
{
ClearTextureFolder();
return;
}
_textureFiles.Clear();
var files = TextureScanner.ScanDirectory(path);
_textureFiles.AddRange(files);
// 全部默认选中
foreach (var f in _textureFiles)
{
f.selected = true;
}
// 更新拖放区域显示
UpdateTextureDropZone(path, _textureFiles.Count);
Log($"扫描完成,找到 {_textureFiles.Count} 个切图文件");
}
///
/// 更新切图文件夹拖放区域显示
///
private void UpdateTextureDropZone(string path, int fileCount)
{
string folderName = Path.GetFileName(path);
if (string.IsNullOrEmpty(folderName))
{
folderName = path;
}
// 显示文件夹图标和名称
var folderIcon = _textureDropZone.Q