refactor(homepage): 新增主页下拉刷新功能,优化密码校验逻辑
1. 新增HomePagePullRefreshTrigger组件实现全局下拉刷新 2. 调整Android最低SDK版本到31 3. 新增密码校验失败的多语言文案 4. 重构密码校验逻辑,添加异常捕获和统一的错误提示 5. 优化首页离线时的页面重置逻辑
This commit is contained in:
parent
8c40683026
commit
e84d72dbf7
@ -199,6 +199,11 @@
|
|||||||
"zh": "账号或密码错误,请重试",
|
"zh": "账号或密码错误,请重试",
|
||||||
"en": "Incorrect account or password. Please try again."
|
"en": "Incorrect account or password. Please try again."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "home_password_check_failed",
|
||||||
|
"zh": "密码错误或校验失败,请重试",
|
||||||
|
"en": "Password incorrect or verification failed. Please try again."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "100041",
|
"key": "100041",
|
||||||
"zh": "该邮箱已绑定至其他账号,请更换邮箱",
|
"zh": "该邮箱已绑定至其他账号,请更换邮箱",
|
||||||
|
|||||||
@ -7955,6 +7955,7 @@ GameObject:
|
|||||||
- component: {fileID: 6551506190466531803}
|
- component: {fileID: 6551506190466531803}
|
||||||
- component: {fileID: 3881773501436982883}
|
- component: {fileID: 3881773501436982883}
|
||||||
- component: {fileID: 6647875750186480783}
|
- component: {fileID: 6647875750186480783}
|
||||||
|
- component: {fileID: 2568189713370643574}
|
||||||
m_Layer: 5
|
m_Layer: 5
|
||||||
m_Name: "\u65E0\u8BBE\u5907"
|
m_Name: "\u65E0\u8BBE\u5907"
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@ -8004,7 +8005,7 @@ MonoBehaviour:
|
|||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
m_Material: {fileID: 0}
|
m_Material: {fileID: 0}
|
||||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||||
m_RaycastTarget: 0
|
m_RaycastTarget: 1
|
||||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||||
m_Maskable: 1
|
m_Maskable: 1
|
||||||
m_OnCullStateChanged:
|
m_OnCullStateChanged:
|
||||||
@ -8020,6 +8021,18 @@ MonoBehaviour:
|
|||||||
m_FillOrigin: 0
|
m_FillOrigin: 0
|
||||||
m_UseSpriteMesh: 0
|
m_UseSpriteMesh: 0
|
||||||
m_PixelsPerUnitMultiplier: 1
|
m_PixelsPerUnitMultiplier: 1
|
||||||
|
--- !u!114 &2568189713370643574
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3582556473535289502}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 432e8b6f5f7dd7a4f94d9c3b85c8b262, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
--- !u!1 &3686991546777705807
|
--- !u!1 &3686991546777705807
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@ -15843,7 +15856,7 @@ RectTransform:
|
|||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0, y: 0}
|
m_AnchorMin: {x: 0, y: 0}
|
||||||
m_AnchorMax: {x: 1, y: 0}
|
m_AnchorMax: {x: 1, y: 0}
|
||||||
m_AnchoredPosition: {x: -9.000002, y: 10}
|
m_AnchoredPosition: {x: -9, y: 10}
|
||||||
m_SizeDelta: {x: -57.999992, y: 55}
|
m_SizeDelta: {x: -57.999992, y: 55}
|
||||||
m_Pivot: {x: 0.5, y: 0}
|
m_Pivot: {x: 0.5, y: 0}
|
||||||
--- !u!222 &3186938245855593830
|
--- !u!222 &3186938245855593830
|
||||||
@ -16935,6 +16948,8 @@ MonoBehaviour:
|
|||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
noDevicePage: {fileID: 3582556473535289502}
|
noDevicePage: {fileID: 3582556473535289502}
|
||||||
normalPage: {fileID: 8115864904787809625}
|
normalPage: {fileID: 8115864904787809625}
|
||||||
|
normalScrollRect: {fileID: 7398385099859628837}
|
||||||
|
pullTriggerDistance: 120
|
||||||
selectDeviceButton: {fileID: 1920078014523356616}
|
selectDeviceButton: {fileID: 1920078014523356616}
|
||||||
selectDevicePage: {fileID: 2340476075600265632}
|
selectDevicePage: {fileID: 2340476075600265632}
|
||||||
selectCountryPage: {fileID: 1196293823344848224, guid: 90501a08c2cf82c4187b5f13c391291f,
|
selectCountryPage: {fileID: 1196293823344848224, guid: 90501a08c2cf82c4187b5f13c391291f,
|
||||||
@ -17117,6 +17132,7 @@ GameObject:
|
|||||||
- component: {fileID: 2697609740981075072}
|
- component: {fileID: 2697609740981075072}
|
||||||
- component: {fileID: 928882153762487195}
|
- component: {fileID: 928882153762487195}
|
||||||
- component: {fileID: 7398385099859628837}
|
- component: {fileID: 7398385099859628837}
|
||||||
|
- component: {fileID: 5876146908749457361}
|
||||||
m_Layer: 5
|
m_Layer: 5
|
||||||
m_Name: "\u6B63\u5E38"
|
m_Name: "\u6B63\u5E38"
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@ -17225,6 +17241,18 @@ MonoBehaviour:
|
|||||||
m_OnValueChanged:
|
m_OnValueChanged:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
|
--- !u!114 &5876146908749457361
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 8115864904787809625}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 432e8b6f5f7dd7a4f94d9c3b85c8b262, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
--- !u!1 &8135258645254993493
|
--- !u!1 &8135258645254993493
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
31
Assets/Scripts/UI/Components/HomePagePullRefreshTrigger.cs
Normal file
31
Assets/Scripts/UI/Components/HomePagePullRefreshTrigger.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using Kill.UI.Pages;
|
||||||
|
|
||||||
|
namespace Kill.UI.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 下拉刷新触发器:挂到主页 ScrollRect(带 Image 的 Graphic)上,接收 EventSystem
|
||||||
|
/// 拖拽事件并转发给 HomePageCtrl。这样在滚动区域内任意位置都可触发下拉刷新。
|
||||||
|
/// </summary>
|
||||||
|
public class HomePagePullRefreshTrigger : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||||
|
{
|
||||||
|
public void OnBeginDrag(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
var ctrl = HomePageCtrl.Instance;
|
||||||
|
if (ctrl != null) ctrl.OnPullRefreshBeginDrag();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDrag(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
var ctrl = HomePageCtrl.Instance;
|
||||||
|
if (ctrl != null) ctrl.OnPullRefreshDrag(eventData.delta.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnEndDrag(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
var ctrl = HomePageCtrl.Instance;
|
||||||
|
if (ctrl != null) ctrl.OnPullRefreshEndDrag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 432e8b6f5f7dd7a4f94d9c3b85c8b262
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -24,34 +24,45 @@ namespace Kill.UI.Pages
|
|||||||
{
|
{
|
||||||
errorText.text="";
|
errorText.text="";
|
||||||
LoadingUI.Show();
|
LoadingUI.Show();
|
||||||
string password = passwordInput != null ? passwordInput.text : "";
|
try
|
||||||
if (string.IsNullOrEmpty(password))
|
|
||||||
{
|
{
|
||||||
errorText.text = LanguageManager.Instance.GetLanguage("100040");
|
string password = passwordInput != null ? passwordInput.text : "";
|
||||||
|
if (string.IsNullOrEmpty(password))
|
||||||
|
{
|
||||||
|
errorText.text = LanguageManager.Instance.GetLanguage("home_password_check_failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PasswordRequest requestData=new PasswordRequest
|
||||||
|
{
|
||||||
|
password=passwordInput.text
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await NetworkCtrl.Instance.Post<LoginResponse>("/api/v1/auth/password/verify", requestData);
|
||||||
|
|
||||||
|
if (response != null && response.Data != null && response.Data.code == 200)
|
||||||
|
{
|
||||||
|
plane.SetActive(false);
|
||||||
|
gameObject.SetActive(false);
|
||||||
|
errorText.text="";
|
||||||
|
passwordInput.text="";
|
||||||
|
UIManager.Instance.isFirstTime=false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorText.text = LanguageManager.Instance.GetLanguage("home_password_check_failed");
|
||||||
|
passwordInput.text="";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[CheckPasswordPage] 校验密码异常: {ex.Message}");
|
||||||
|
errorText.text = LanguageManager.Instance.GetLanguage("home_password_check_failed");
|
||||||
|
if (passwordInput != null) passwordInput.text = "";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// 无论成功/失败/异常都关闭 Loading,避免一直挂着
|
||||||
LoadingUI.Hide();
|
LoadingUI.Hide();
|
||||||
return;
|
|
||||||
}
|
|
||||||
PasswordRequest requestData=new PasswordRequest
|
|
||||||
{
|
|
||||||
password=passwordInput.text
|
|
||||||
};
|
|
||||||
|
|
||||||
var response = await NetworkCtrl.Instance.Post<LoginResponse>("/api/v1/auth/password/verify", requestData);
|
|
||||||
|
|
||||||
LoadingUI.Hide();
|
|
||||||
|
|
||||||
if (response.Data.code == 200)
|
|
||||||
{
|
|
||||||
plane.SetActive(false);
|
|
||||||
gameObject.SetActive(false);
|
|
||||||
errorText.text="";
|
|
||||||
passwordInput.text="";
|
|
||||||
UIManager.Instance.isFirstTime=false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errorText.text = LanguageManager.Instance.GetLanguage("100040");
|
|
||||||
passwordInput.text="";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,13 @@ namespace Kill.UI.Pages
|
|||||||
|
|
||||||
public GameObject noDevicePage;
|
public GameObject noDevicePage;
|
||||||
public GameObject normalPage;
|
public GameObject normalPage;
|
||||||
|
// 下拉刷新:滚动区域 ScrollRect(用于判断内容是否在最顶部);无设备页可不填
|
||||||
|
public ScrollRect normalScrollRect;
|
||||||
|
// 下拉刷新参数
|
||||||
|
[SerializeField] private float pullTriggerDistance = 120f; // 触发刷新的拖动距离(像素)
|
||||||
|
private float pullDistance; // 当前累积下拉距离
|
||||||
|
private bool isPullingAtTop; // 拖动开始时是否在内容顶部
|
||||||
|
private bool isRefreshing;
|
||||||
// 存储待执行的UI更新操作
|
// 存储待执行的UI更新操作
|
||||||
private List<Action> pendingUIUpdates = new List<Action>();
|
private List<Action> pendingUIUpdates = new List<Action>();
|
||||||
public Button selectDeviceButton;
|
public Button selectDeviceButton;
|
||||||
@ -477,6 +484,7 @@ namespace Kill.UI.Pages
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 网络断开:立刻把 WiFi 设备标离线并刷新 UI
|
/// 网络断开:立刻把 WiFi 设备标离线并刷新 UI
|
||||||
|
/// 设备离线导致的 UI 重置由后台轮询触发,避免此处重复 Init
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnNetworkDisconnectedHandler(object sender, EventArgs e)
|
private void OnNetworkDisconnectedHandler(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
@ -1878,11 +1886,14 @@ namespace Kill.UI.Pages
|
|||||||
// WiFi和蓝牙都断开
|
// WiFi和蓝牙都断开
|
||||||
if (wasOnline)
|
if (wasOnline)
|
||||||
{
|
{
|
||||||
Debug.Log("[HomePageCtrl] 设备WiFi离线");
|
Debug.Log("[HomePageCtrl] 设备WiFi离线,重新加载首页");
|
||||||
pendingUIUpdates.Add(() =>
|
pendingUIUpdates.Add(() =>
|
||||||
{
|
{
|
||||||
deviceState?.UpdateDeviceState(hasBluetooth, hasWifi);
|
deviceState?.UpdateDeviceState(hasBluetooth, hasWifi);
|
||||||
});
|
});
|
||||||
|
// 设备由在线变为离线且无蓝牙连接 → 直接重新加载首页
|
||||||
|
UIManager.Instance.OpenMainPage(UIManager.PageName.homePage);
|
||||||
|
return; // 旧页面即将销毁,结束轮询
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1938,6 +1949,62 @@ namespace Kill.UI.Pages
|
|||||||
pendingUIUpdates.Add(action);
|
pendingUIUpdates.Add(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region 下拉刷新(由 HomePagePullRefreshTrigger 组件转发调用)
|
||||||
|
|
||||||
|
public void OnPullRefreshBeginDrag()
|
||||||
|
{
|
||||||
|
if (isRefreshing) return;
|
||||||
|
isPullingAtTop = IsContentAtTop();
|
||||||
|
pullDistance = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPullRefreshDrag(float deltaY)
|
||||||
|
{
|
||||||
|
if (isRefreshing || !isPullingAtTop) return;
|
||||||
|
if (deltaY < 0)
|
||||||
|
{
|
||||||
|
pullDistance += -deltaY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPullRefreshEndDrag()
|
||||||
|
{
|
||||||
|
if (isRefreshing) return;
|
||||||
|
if (pullDistance >= pullTriggerDistance)
|
||||||
|
{
|
||||||
|
TriggerPullRefresh();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResetPullRefreshUI();
|
||||||
|
}
|
||||||
|
isPullingAtTop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断 ScrollRect 内容是否已在顶部(无 ScrollRect 时视为可下拉)
|
||||||
|
/// </summary>
|
||||||
|
private bool IsContentAtTop()
|
||||||
|
{
|
||||||
|
if (normalScrollRect == null) return true;
|
||||||
|
return normalScrollRect.verticalNormalizedPosition >= 0.999f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerPullRefresh()
|
||||||
|
{
|
||||||
|
if (isRefreshing) return;
|
||||||
|
isRefreshing = true;
|
||||||
|
// 重新加载首页(销毁旧实例并实例化新实例,触发 Start/Init 重新跑)
|
||||||
|
UIManager.Instance.OpenMainPage(UIManager.PageName.homePage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetPullRefreshUI()
|
||||||
|
{
|
||||||
|
pullDistance = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从WiFi刷新主页定时任务显示
|
/// 从WiFi刷新主页定时任务显示
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -173,7 +173,7 @@ PlayerSettings:
|
|||||||
tvOS: 0
|
tvOS: 0
|
||||||
overrideDefaultApplicationIdentifier: 1
|
overrideDefaultApplicationIdentifier: 1
|
||||||
AndroidBundleVersionCode: 4
|
AndroidBundleVersionCode: 4
|
||||||
AndroidMinSdkVersion: 33
|
AndroidMinSdkVersion: 31
|
||||||
AndroidTargetSdkVersion: 36
|
AndroidTargetSdkVersion: 36
|
||||||
AndroidPreferredInstallLocation: 0
|
AndroidPreferredInstallLocation: 0
|
||||||
aotOptions:
|
aotOptions:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user