对 DisplayItemInfoForm 按 5 层 UI 规范做结构整理

- 删除无用的 DisplayItemInfoUseCase,转为轻量 UI(Controller + Context + View)
- RawData 类型安全:object Data 拆分为 WeaponData + PropItem
- View 事件规范:回收按钮改用 UI 专用事件,由 Controller 通过回调转发
- 简化 _locked 状态管理,统一在 CloseUIAsync 中清理
This commit is contained in:
SepComet 2026-06-15 11:03:31 +08:00
parent 9dd4f57e6a
commit bba17bbac2
11 changed files with 107 additions and 173 deletions

View File

@ -0,0 +1,36 @@
using GameFramework;
using GameFramework.Event;
namespace SepCore.Event
{
public class DisplayItemInfoRecycleEventArgs : GameEventArgs
{
public static readonly int EventId = typeof(DisplayItemInfoRecycleEventArgs).GetHashCode();
public override int Id => EventId;
public int Index;
public int Price;
public DisplayItemInfoRecycleEventArgs()
{
Index = -1;
Price = 0;
}
public static DisplayItemInfoRecycleEventArgs Create(int index, int price)
{
var args = ReferencePool.Acquire<DisplayItemInfoRecycleEventArgs>();
args.Index = index;
args.Price = price;
return args;
}
public override void Clear()
{
Index = -1;
Price = 0;
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 48c1f32fe95e20c43b8015ac1438d54e guid: 9b959bd91b2fade47990fed6ce898f4f
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -1,37 +0,0 @@
using GameFramework;
using GameFramework.Event;
namespace SepCore.Event
{
public class ShopWeaponRecycleEventArgs : GameEventArgs
{
public static readonly int EventId = typeof(ShopWeaponRecycleEventArgs).GetHashCode();
public override int Id => EventId;
public int Index { get; private set; }
public int Price { get; private set; }
public ShopWeaponRecycleEventArgs()
{
Index = -1;
Price = 0;
}
public static ShopWeaponRecycleEventArgs Create(int index, int price)
{
var args = ReferencePool.Acquire<ShopWeaponRecycleEventArgs>();
args.Index = index;
args.Price = price;
return args;
}
public override void Clear()
{
Index = -1;
Price = 0;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 2a11aec04bee4abf85e3df1d495e9f84
timeCreated: 1771487051

View File

@ -18,6 +18,7 @@ namespace SepCore.UI
public string Description; public string Description;
public int Price; public int Price;
public Vector3 TargetPos; public Vector3 TargetPos;
public bool ShowRecycleButton;
public DisplayItemInfoContext() public DisplayItemInfoContext()
{ {
@ -29,6 +30,7 @@ namespace SepCore.UI
Description = string.Empty; Description = string.Empty;
Price = 0; Price = 0;
TargetPos = Vector3.zero; TargetPos = Vector3.zero;
ShowRecycleButton = false;
} }
public DisplayItemInfoContext(DisplayItemInfoRawData rawData) public DisplayItemInfoContext(DisplayItemInfoRawData rawData)
@ -38,6 +40,7 @@ namespace SepCore.UI
Rarity = rawData.Rarity; Rarity = rawData.Rarity;
Price = rawData.Price; Price = rawData.Price;
TargetPos = rawData.TargetPos; TargetPos = rawData.TargetPos;
ShowRecycleButton = rawData.OnRecycle != null;
if (ItemType == ItemType.None) if (ItemType == ItemType.None)
{ {
IconAssetName = string.Empty; IconAssetName = string.Empty;
@ -45,14 +48,14 @@ namespace SepCore.UI
} }
else if (ItemType == ItemType.Weapon) else if (ItemType == ItemType.Weapon)
{ {
var data = (WeaponData)rawData.Data; var data = rawData.WeaponData;
IconAssetName = data.IconAssetName; IconAssetName = data.IconAssetName;
Title = data.Title; Title = data.Title;
Description = ItemDescUtility.CreateWeaponDescription(data); Description = ItemDescUtility.CreateWeaponDescription(data);
} }
else else
{ {
var data = (PropItem)rawData.Data; var data = rawData.PropData;
IconAssetName = data.IconAssetName; IconAssetName = data.IconAssetName;
Title = data.Title; Title = data.Title;
Description = ItemDescUtility.CreatePropDescription(data); Description = ItemDescUtility.CreatePropDescription(data);

View File

@ -1,3 +1,4 @@
using System;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using SepCore.Event; using SepCore.Event;
using SepCore.Definition; using SepCore.Definition;
@ -12,17 +13,20 @@ namespace SepCore.UI
protected override UIFormType UIFormType => UIFormType.DisplayItemInfoForm; protected override UIFormType UIFormType => UIFormType.DisplayItemInfoForm;
private bool _locked = false; private bool _locked = false;
private Action<int, int> _onRecycle;
protected override void SubscribeCustomEvents() protected override void SubscribeCustomEvents()
{ {
GameEntry.Event.Subscribe(DisplayItemInfoLockEventArgs.EventId, DisplayItemInfoLock); GameEntry.Event.Subscribe(DisplayItemInfoLockEventArgs.EventId, DisplayItemInfoLock);
GameEntry.Event.Subscribe(DisplayItemInfoHideEventArgs.EventId, DisplayItemInfoHide); GameEntry.Event.Subscribe(DisplayItemInfoHideEventArgs.EventId, DisplayItemInfoHide);
GameEntry.Event.Subscribe(DisplayItemInfoRecycleEventArgs.EventId, DisplayItemInfoRecycle);
} }
protected override void UnsubscribeCustomEvents() protected override void UnsubscribeCustomEvents()
{ {
GameEntry.Event.Unsubscribe(DisplayItemInfoLockEventArgs.EventId, DisplayItemInfoLock); GameEntry.Event.Unsubscribe(DisplayItemInfoLockEventArgs.EventId, DisplayItemInfoLock);
GameEntry.Event.Unsubscribe(DisplayItemInfoHideEventArgs.EventId, DisplayItemInfoHide); GameEntry.Event.Unsubscribe(DisplayItemInfoHideEventArgs.EventId, DisplayItemInfoHide);
GameEntry.Event.Unsubscribe(DisplayItemInfoRecycleEventArgs.EventId, DisplayItemInfoRecycle);
} }
protected override void RefreshUI(DisplayItemInfoForm form, DisplayItemInfoContext context) protected override void RefreshUI(DisplayItemInfoForm form, DisplayItemInfoContext context)
@ -69,21 +73,22 @@ namespace SepCore.UI
return; return;
} }
_locked = false; _onRecycle = rawData.OnRecycle;
await OpenFormAsync(context, timeout); await OpenFormAsync(context, timeout);
} }
public override async UniTask CloseUIAsync(object userData = null, float timeout = 30f) public override async UniTask CloseUIAsync(object userData = null, float timeout = 30f)
{ {
_locked = false; _locked = false;
_onRecycle = null;
await base.CloseUIAsync(userData, timeout); await base.CloseUIAsync(userData, timeout);
} }
public override void BindUseCase(IUIUseCase useCase) public override void BindUseCase(IUIUseCase useCase)
{ {
if (useCase is not DisplayItemInfoUseCase) if (useCase != null)
{ {
Log.Error("DisplayItemInfoForm.BindUseCase() useCase is invalid."); Log.Warning("DisplayItemInfoController does not use a use case.");
} }
} }
@ -121,17 +126,24 @@ namespace SepCore.UI
return; return;
} }
if (args.Force)
{
GameEntry.UIRouter.CloseUIAsync(UIFormType.DisplayItemInfoForm).Forget();
_locked = false;
return;
}
if (_locked && !args.Force) return; if (_locked && !args.Force) return;
GameEntry.UIRouter.CloseUIAsync(UIFormType.DisplayItemInfoForm).Forget(); GameEntry.UIRouter.CloseUIAsync(UIFormType.DisplayItemInfoForm).Forget();
_locked = false; }
private void DisplayItemInfoRecycle(object sender, GameEventArgs e)
{
if (e is not DisplayItemInfoRecycleEventArgs args)
{
return;
}
if (!IsCurrentFormSender(sender))
{
return;
}
_onRecycle?.Invoke(args.Index, args.Price);
} }
#endregion #endregion

View File

@ -62,7 +62,7 @@ namespace SepCore.UI
if (_recycleButton != null) if (_recycleButton != null)
{ {
_recycleButton.gameObject.SetActive(_context.ItemType == ItemType.Weapon); _recycleButton.gameObject.SetActive(_context.ShowRecycleButton);
} }
if (_iconArea != null && !string.IsNullOrEmpty(_context.IconAssetName)) if (_iconArea != null && !string.IsNullOrEmpty(_context.IconAssetName))
@ -191,8 +191,8 @@ namespace SepCore.UI
public void OnRecycleButtonClick() public void OnRecycleButtonClick()
{ {
if (_context == null || _context.ItemType != ItemType.Weapon) return; if (_context == null) return;
GameEntry.Event.Fire(this, ShopWeaponRecycleEventArgs.Create(_context.Index, _context.Price)); GameEntry.Event.Fire(this, DisplayItemInfoRecycleEventArgs.Create(_context.Index, _context.Price));
} }
public void OnCancelButtonClick() public void OnCancelButtonClick()

View File

@ -26,7 +26,6 @@ namespace SepCore.UI
{ {
GameEntry.Event.Subscribe(RefreshEventArgs.EventId, Refresh); GameEntry.Event.Subscribe(RefreshEventArgs.EventId, Refresh);
GameEntry.Event.Subscribe(ShopPurchaseEventArgs.EventId, ShopPurchase); GameEntry.Event.Subscribe(ShopPurchaseEventArgs.EventId, ShopPurchase);
GameEntry.Event.Subscribe(ShopWeaponRecycleEventArgs.EventId, WeaponRecycle);
GameEntry.Event.Subscribe(ShopContinueEventArgs.EventId, ShopContinue); GameEntry.Event.Subscribe(ShopContinueEventArgs.EventId, ShopContinue);
GameEntry.Event.Subscribe(DisplayItemShowEventArgs.EventId, DisplayItemShow); GameEntry.Event.Subscribe(DisplayItemShowEventArgs.EventId, DisplayItemShow);
} }
@ -35,7 +34,6 @@ namespace SepCore.UI
{ {
GameEntry.Event.Unsubscribe(RefreshEventArgs.EventId, Refresh); GameEntry.Event.Unsubscribe(RefreshEventArgs.EventId, Refresh);
GameEntry.Event.Unsubscribe(ShopPurchaseEventArgs.EventId, ShopPurchase); GameEntry.Event.Unsubscribe(ShopPurchaseEventArgs.EventId, ShopPurchase);
GameEntry.Event.Unsubscribe(ShopWeaponRecycleEventArgs.EventId, WeaponRecycle);
GameEntry.Event.Unsubscribe(ShopContinueEventArgs.EventId, ShopContinue); GameEntry.Event.Unsubscribe(ShopContinueEventArgs.EventId, ShopContinue);
GameEntry.Event.Unsubscribe(DisplayItemShowEventArgs.EventId, DisplayItemShow); GameEntry.Event.Unsubscribe(DisplayItemShowEventArgs.EventId, DisplayItemShow);
} }
@ -379,10 +377,11 @@ namespace SepCore.UI
{ {
TargetPos = targetPos, TargetPos = targetPos,
Index = index, Index = index,
Data = weapon.WeaponData, WeaponData = weapon.WeaponData,
Rarity = weapon.WeaponData.Rarity, Rarity = weapon.WeaponData.Rarity,
ItemType = ItemType.Weapon, ItemType = ItemType.Weapon,
Price = Mathf.FloorToInt(weapon.WeaponData.Price * Context.WeaponRecycleRate), Price = Mathf.FloorToInt(weapon.WeaponData.Price * Context.WeaponRecycleRate),
OnRecycle = (recycleIndex, recyclePrice) => RecycleWeapon(recycleIndex, recyclePrice),
}; };
return true; return true;
} }
@ -414,7 +413,7 @@ namespace SepCore.UI
{ {
TargetPos = targetPos, TargetPos = targetPos,
Index = index, Index = index,
Data = propItem, PropData = propItem,
Rarity = propItem.Rarity, Rarity = propItem.Rarity,
ItemType = ItemType.Prop, ItemType = ItemType.Prop,
Price = 0 Price = 0
@ -422,6 +421,30 @@ namespace SepCore.UI
return true; return true;
} }
private void RecycleWeapon(int index, int price)
{
if (_useCase == null || Context == null)
{
Log.Error("ShopFormController.RecycleWeapon() controller state is invalid.");
return;
}
bool success = _useCase.TryRecycleWeapon(index, price);
if (!success)
{
return;
}
if (Context.WeaponListContext != null)
{
int currentCount = Mathf.Max(0, Context.WeaponListContext.CurrentCount - 1);
Context.WeaponListContext.CurrentCount = currentCount;
}
Form?.RemoveWeaponDisplayItem(index);
GameEntry.Event.Fire(this, DisplayItemInfoHideEventArgs.Create(true));
}
#endregion #endregion
#region Event Handlers #region Event Handlers
@ -519,40 +542,6 @@ namespace SepCore.UI
GameEntry.UIRouter.OpenUIAsync(UIFormType.DisplayItemInfoForm, rawData).Forget(); GameEntry.UIRouter.OpenUIAsync(UIFormType.DisplayItemInfoForm, rawData).Forget();
} }
private void WeaponRecycle(object sender, GameEventArgs e)
{
if (e is not ShopWeaponRecycleEventArgs args)
{
return;
}
if (sender is not DisplayItemInfoForm)
{
return;
}
if (_useCase == null || Context == null)
{
Log.Error("ShopFormController.WeaponRecycle() controller state is invalid.");
return;
}
bool success = _useCase.TryRecycleWeapon(args.Index, args.Price);
if (!success)
{
return;
}
if (Context.WeaponListContext != null)
{
int currentCount = Mathf.Max(0, Context.WeaponListContext.CurrentCount - 1);
Context.WeaponListContext.CurrentCount = currentCount;
}
Form?.RemoveWeaponDisplayItem(args.Index);
GameEntry.Event.Fire(this, DisplayItemInfoHideEventArgs.Create(true));
}
#endregion #endregion
} }
} }

View File

@ -1,4 +1,7 @@
using System;
using SepCore.Definition; using SepCore.Definition;
using SepCore.Entity;
using SepCore.Entity.Weapon;
using UnityEngine; using UnityEngine;
namespace SepCore.UI namespace SepCore.UI
@ -9,14 +12,16 @@ namespace SepCore.UI
Weapon, Weapon,
Prop Prop
} }
public class DisplayItemInfoRawData public class DisplayItemInfoRawData
{ {
public int Index; public int Index;
public object Data; public WeaponData WeaponData;
public PropItem PropData;
public ItemType ItemType; public ItemType ItemType;
public ItemRarity Rarity; public ItemRarity Rarity;
public int Price; public int Price;
public Vector3 TargetPos; public Vector3 TargetPos;
public Action<int, int> OnRecycle;
} }
} }

View File

@ -1,68 +0,0 @@
using SepCore.Definition;
using SepCore.Entity;
using SepCore.CustomUtility;
using SepCore.Entity.Weapon;
namespace SepCore.UI
{
public class DisplayItemInfoUseCase : IUIUseCase
{
private readonly Player _player;
private Player Player => _player;
public DisplayItemInfoUseCase(Player player)
{
_player = player;
}
public DisplayItemInfoRawData CreateModel(int index, bool isWeapon)
{
if (index < 0 || Player == null)
{
return null;
}
if (isWeapon)
{
var weapons = Player.Weapons;
if (weapons == null || index >= weapons.Count)
{
return null;
}
WeaponBase weapon = weapons[index];
if (weapon == null || weapon.WeaponData == null)
{
return null;
}
return new DisplayItemInfoRawData
{
Data = weapon.WeaponData,
ItemType = ItemType.Weapon,
Price = 0,
};
}
var props = Player.Props;
if (props == null || index >= props.Count)
{
return null;
}
PropItem prop = props[index];
if (prop == null)
{
return null;
}
return new DisplayItemInfoRawData
{
Data = prop,
ItemType = ItemType.Prop,
Price = 0,
};
}
}
}

View File

@ -103,15 +103,11 @@ MonoBehaviour:
m_Interactable: 1 m_Interactable: 1
m_TargetGraphic: {fileID: 0} m_TargetGraphic: {fileID: 0}
m_OnClick: m_OnClick:
m_PersistentCalls:
m_Calls: []
_background: {fileID: 2138039234147195629}
_onPointerEnterAction:
m_PersistentCalls: m_PersistentCalls:
m_Calls: m_Calls:
- m_Target: {fileID: 8108003253794388876} - m_Target: {fileID: 8108003253794388876}
m_TargetAssemblyTypeName: UI.DisplayItem, Assembly-CSharp m_TargetAssemblyTypeName: SepCore.UI.DisplayItem, SepCore.Presentation
m_MethodName: OnItemInfoShow m_MethodName: OnItemInfoLock
m_Mode: 1 m_Mode: 1
m_Arguments: m_Arguments:
m_ObjectArgument: {fileID: 0} m_ObjectArgument: {fileID: 0}
@ -121,12 +117,13 @@ MonoBehaviour:
m_StringArgument: m_StringArgument:
m_BoolArgument: 0 m_BoolArgument: 0
m_CallState: 2 m_CallState: 2
_onClickAction: _background: {fileID: 2138039234147195629}
_onPointerEnterAction:
m_PersistentCalls: m_PersistentCalls:
m_Calls: m_Calls:
- m_Target: {fileID: 8108003253794388876} - m_Target: {fileID: 8108003253794388876}
m_TargetAssemblyTypeName: UI.DisplayItem, Assembly-CSharp m_TargetAssemblyTypeName: UI.DisplayItem, Assembly-CSharp
m_MethodName: OnItemInfoLock m_MethodName: OnItemInfoShow
m_Mode: 1 m_Mode: 1
m_Arguments: m_Arguments:
m_ObjectArgument: {fileID: 0} m_ObjectArgument: {fileID: 0}