From d1a0f1d2842f78b5232948e8e33e7e83d954a6cb Mon Sep 17 00:00:00 2001
From: lcy <1459594991@qq.com>
Date: 星期二, 27 一月 2026 20:31:18 +0800
Subject: [PATCH] 449 查看他人-客户端 新增命格、古宝信息,战场点击查看双方武将或npc信息

---
 Main/System/Attribute/SimpleAttributeAttrCell.cs           |   26 
 Main/System/Battle/BattleConst.cs                          |    5 
 Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs         |  326 ++++++++++
 Main/Common/EventName.cs                                   |    2 
 Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs          |  210 +++++++
 Main/System/Attribute/SimpleMinggeAttributeWin.cs          |  101 +++
 Main/System/Horse/HorseSkinWin.cs                          |    2 
 Main/System/Attribute/AttributeManager.cs                  |  109 +++
 Main/System/OtherPlayerDetail/OtherHeroDetailWin.cs        |  286 ++++++++
 Main/System/OtherPlayerDetail/OtherPlayerDetailWin.cs      |   56 +
 Main/System/Gubao/GubaoListWin.cs                          |    2 
 Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs.meta    |   11 
 Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs.meta     |   11 
 Main/System/Battle/BattleObject/BattleObject.cs            |   38 +
 Main/System/PhantasmPavilion/PhantasmPavilionFaceWin.cs    |    2 
 Main/System/OtherPlayerDetail/OtherHeroFightingCardItem.cs |    3 
 Main/System/Attribute/SimpleMinggeAttributeCell.cs         |   30 +
 Main/System/WarlordPavilion/WarlordPavilionWin.cs          |    2 
 Main/System/Attribute/SimpleAttributeWin.cs                |   49 +
 Main/System/PhantasmPavilion/PhantasmPavilionTilteWin.cs   |    2 
 Main/System/ViewNPC/ViewNPCManager.cs                      |  172 +++++
 Main/Utility/EnumHelper.cs                                 |    2 
 Main/System/Mingge/MinggeWin.cs                            |    8 
 Main/System/Attribute/SimpleMinggeAttributeWin.cs.meta     |   11 
 Main/System/Battle/BattleManager.cs                        |    6 
 Main/System/OtherPlayerDetail/OtherPlayerDetailManager.cs  |  219 +++++-
 Main/System/PhantasmPavilion/PhantasmPavilionModelWin.cs   |    2 
 Main/System/Attribute/SimpleAttributeWin.cs.meta           |   11 
 Main/System/Attribute/SimpleAttributeAttrItem.cs           |   19 
 Main/System/BeautyMM/BeautyMMListWin.cs                    |    2 
 Main/System/Attribute/SimpleAttributeAttrItem.cs.meta      |   11 
 Main/System/Attribute/SimpleAttributeAttrCell.cs.meta      |   11 
 Main/System/Attribute/SimpleMinggeAttributeCell.cs.meta    |   11 
 33 files changed, 1,654 insertions(+), 104 deletions(-)

diff --git a/Main/Common/EventName.cs b/Main/Common/EventName.cs
index f6a6170..ca978ab 100644
--- a/Main/Common/EventName.cs
+++ b/Main/Common/EventName.cs
@@ -18,4 +18,6 @@
 	public const string RECORDPLAYER_END = "RECORDPLAYER_END"; //鎴樻枟灏忕墖娈电粨鏉�
 
 	public const string BATTLE_TIANZI_REFRESH_HP = "BATTLE_TIANZI_REFRESH_HP";//澶╁瓙鑰冮獙 鍒锋柊琛�閲�
+	
+	public const string BATTLE_CLICK_HERO = "BATTLE_CLICK_HERO";//鐐瑰嚮鑻遍泟
 }
\ No newline at end of file
diff --git a/Main/System/Attribute/AttributeManager.cs b/Main/System/Attribute/AttributeManager.cs
index 2bd08f3..bcd92ba 100644
--- a/Main/System/Attribute/AttributeManager.cs
+++ b/Main/System/Attribute/AttributeManager.cs
@@ -11,4 +11,113 @@
             UIManager.Instance.OpenWindow<TotalAttributeWin>(functionOrder);
         }
     }
+
+    public readonly int SimpleAttributeMaxRowCnt = 2;
+    public Dictionary<int, long> simpleAttrDict = new Dictionary<int, long>();
+    public List<int> simpleAttrSortList = new List<int>();
+    public string simpleAttrTitleName = string.Empty;
+    public void OpenSimpleAttributeWin(Dictionary<int, long> simpleAttrs, string titleNameKey = "")
+    {
+        simpleAttrDict = FilterValidAttributes(simpleAttrs);
+        simpleAttrSortList = SortAttrId(simpleAttrDict);
+        simpleAttrTitleName = Language.Get(string.IsNullOrEmpty(titleNameKey) ? "AttributeTitle00" : titleNameKey);
+        if (!UIManager.Instance.IsOpened<SimpleAttributeWin>())
+        {
+            UIManager.Instance.OpenWindow<SimpleAttributeWin>();
+        }
+    }
+
+    // 鍛芥牸涓撶敤
+    public Dictionary<int, int> skillDic = new Dictionary<int, int>();
+    public List<int> skillList = new List<int>();
+    public void OpenMinggeSimpleAttributeWin(Dictionary<int, long> simpleAttrs, Dictionary<int, int> skills)
+    {
+
+        if (skills.IsNullOrEmpty())
+        {
+            OpenSimpleAttributeWin(simpleAttrs, "AttributeTitle03");
+            return;
+        }
+        simpleAttrDict = FilterValidAttributes(simpleAttrs);
+        simpleAttrSortList = SortAttrId(simpleAttrDict);
+        simpleAttrTitleName = Language.Get("AttributeTitle03");
+        skillDic = skills;
+        skillList = new List<int>(skillDic.Keys);
+        skillList.Sort();
+        if (!UIManager.Instance.IsOpened<SimpleMinggeAttributeWin>())
+        {
+            UIManager.Instance.OpenWindow<SimpleMinggeAttributeWin>();
+        }
+    }
+
+
+    public bool TryGetInfoBySkillID(int skillTypeID, int lv, out string name, out string desc)
+    {
+        name = string.Empty;
+        desc = string.Empty;
+        if (!SkillConfig.HasKey(skillTypeID))
+            return false;
+        SkillConfig config = SkillConfig.Get(skillTypeID + lv - 1);
+        name = Language.Get("OtherPlayerDetail14", Language.Get($"MinggeSkillType_{skillTypeID}"), lv);
+        desc = config.Description;
+        return true;
+    }
+
+
+    /// <summary>
+    /// 杩囨护鏈夋晥鐨勫睘鎬э細绉婚櫎value<=0鎴杝howType<=0鎴栦笉鍦ㄩ厤缃〃涓殑椤�
+    /// </summary>
+    private Dictionary<int, long> FilterValidAttributes(Dictionary<int, long> dict)
+    {
+        if (dict == null)
+            return null;
+
+        var result = new Dictionary<int, long>();
+        foreach (var kvp in dict)
+        {
+            if (!PlayerPropertyConfig.HasKey(kvp.Key))
+                continue;
+            PlayerPropertyConfig config = PlayerPropertyConfig.Get(kvp.Key);
+            if (kvp.Value > 0 && config.showType > 0 && config.showType < 5)
+            {
+                result.Add(kvp.Key, kvp.Value);
+            }
+        }
+        return result;
+    }
+
+    /// <summary>
+    /// 鏍规嵁PlayerPropertyConfig鐨剆howType鍜宻howSequence瀵瑰睘鎬D杩涜鎺掑簭
+    /// 鎺掑簭瑙勫垯锛氬厛鎸塻howType鍗囧簭锛宻howType鐩稿悓鎸塻howSequence鍗囧簭锛宻howSequence鐩稿悓鎸塈D鍗囧簭
+    /// </summary>
+    private List<int> SortAttrId(Dictionary<int, long> dict)
+    {
+        if (dict == null)
+            return null;
+        var list = new List<int>(dict.Keys);
+        list.Sort((a, b) =>
+        {
+            bool hasKeyA = PlayerPropertyConfig.HasKey(a);
+            bool hasKeyB = PlayerPropertyConfig.HasKey(b);
+            // 濡傛灉涓�涓湪琛ㄤ腑涓�涓笉鍦紝灏嗕笉鍦ㄨ〃鐨勬帓鍦ㄥ悗闈�
+            if (hasKeyA != hasKeyB)
+                return hasKeyA.CompareTo(hasKeyB);
+
+            var configA = PlayerPropertyConfig.Get(a);
+            var configB = PlayerPropertyConfig.Get(b);
+            int showTypeA = configA?.showType ?? 0;
+            int showTypeB = configB?.showType ?? 0;
+
+            if (showTypeA != showTypeB)
+                return showTypeA.CompareTo(showTypeB);
+
+            int showSequenceA = configA?.showSequence ?? 0;
+            int showSequenceB = configB?.showSequence ?? 0;
+            if (showSequenceA != showSequenceB)
+                return showSequenceA.CompareTo(showSequenceB);
+
+            return a.CompareTo(b);
+        });
+        return list;
+    }
 }
\ No newline at end of file
diff --git a/Main/System/Attribute/SimpleAttributeAttrCell.cs b/Main/System/Attribute/SimpleAttributeAttrCell.cs
new file mode 100644
index 0000000..6d806ef
--- /dev/null
+++ b/Main/System/Attribute/SimpleAttributeAttrCell.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using UnityEngine;
+public class SimpleAttributeAttrCell : MonoBehaviour
+{
+    [SerializeField] List<AttrTextItem> items;
+    public void Display(int rowIndex, List<int> simpleAttrList, Dictionary<int, long> simpleAttrDict)
+    {
+        if (simpleAttrList.IsNullOrEmpty() || simpleAttrDict.IsNullOrEmpty())
+            return;
+
+        for (int i = 0; i < items.Count; i++)
+        {
+            int index = rowIndex * AttributeManager.Instance.SimpleAttributeMaxRowCnt + i;
+
+            if (index < simpleAttrList.Count)
+            {
+                items[i].SetActive(true);
+                items[i].Display(simpleAttrList[index], simpleAttrDict);
+            }
+            else
+            {
+                items[i].SetActive(false);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Main/System/Attribute/SimpleAttributeAttrCell.cs.meta b/Main/System/Attribute/SimpleAttributeAttrCell.cs.meta
new file mode 100644
index 0000000..756d844
--- /dev/null
+++ b/Main/System/Attribute/SimpleAttributeAttrCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f0453564687973b46a145f72b051d463
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Attribute/SimpleAttributeAttrItem.cs b/Main/System/Attribute/SimpleAttributeAttrItem.cs
new file mode 100644
index 0000000..e923135
--- /dev/null
+++ b/Main/System/Attribute/SimpleAttributeAttrItem.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using UnityEngine;
+public class AttrTextItem : MonoBehaviour
+{
+    [SerializeField] TextEx attrName;
+    [SerializeField] TextEx attrValue;
+    public void Display(int attrId, Dictionary<int, long> simpleAttrDict)
+    {
+        attrName.SetActive(false);
+        attrValue.SetActive(false);
+        if (simpleAttrDict == null || !simpleAttrDict.TryGetValue(attrId, out long value) || !PlayerPropertyConfig.HasKey(attrId))
+            return;
+        PlayerPropertyConfig config = PlayerPropertyConfig.Get(attrId);
+        attrName.SetActive(true);
+        attrValue.SetActive(true);
+        attrName.text = config.ShowName;
+        attrValue.text = StringUtility.Concat("+", PlayerPropertyConfig.GetValueDescription(attrId, value));
+    }
+}
\ No newline at end of file
diff --git a/Main/System/Attribute/SimpleAttributeAttrItem.cs.meta b/Main/System/Attribute/SimpleAttributeAttrItem.cs.meta
new file mode 100644
index 0000000..fb73c2d
--- /dev/null
+++ b/Main/System/Attribute/SimpleAttributeAttrItem.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 86a66a7462e49f1488b07a61f766fcee
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Attribute/SimpleAttributeWin.cs b/Main/System/Attribute/SimpleAttributeWin.cs
new file mode 100644
index 0000000..1bb6c76
--- /dev/null
+++ b/Main/System/Attribute/SimpleAttributeWin.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+public class SimpleAttributeWin : UIBase
+{
+    [SerializeField] Transform noAttr;
+    [SerializeField] TextEx title;
+    [SerializeField] ScrollerController scroller;
+    Dictionary<int, long> simpleAttrDict;
+    List<int> simpleAttrSortList;
+    string simpleAttrTitleName;
+    protected override void OnPreOpen()
+    {
+        simpleAttrDict = AttributeManager.Instance.simpleAttrDict;
+        simpleAttrSortList = AttributeManager.Instance.simpleAttrSortList;
+        simpleAttrTitleName = AttributeManager.Instance.simpleAttrTitleName;
+        
+        scroller.OnRefreshCell += OnRefreshCell;
+        title.text = simpleAttrTitleName;
+        CreateScroller();
+    }
+
+    protected override void OnPreClose()
+    {
+        scroller.OnRefreshCell -= OnRefreshCell;
+    }
+
+    private void OnRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<SimpleAttributeAttrCell>();
+        _cell?.Display(cell.index, simpleAttrSortList, simpleAttrDict);
+    }
+
+    private void CreateScroller()
+    {
+        scroller.Refresh();
+        noAttr.SetActive(simpleAttrSortList.IsNullOrEmpty());
+        if (simpleAttrSortList != null)
+        {
+            int rowCount = (int)Mathf.Ceil((float)simpleAttrSortList.Count / AttributeManager.Instance.SimpleAttributeMaxRowCnt);
+            for (int i = 0; i < rowCount; i++)
+            {
+                scroller.AddCell(ScrollerDataType.Header, i);
+            }
+        }
+        scroller.Restart();
+    }
+}
\ No newline at end of file
diff --git a/Main/System/Attribute/SimpleAttributeWin.cs.meta b/Main/System/Attribute/SimpleAttributeWin.cs.meta
new file mode 100644
index 0000000..a8de3e4
--- /dev/null
+++ b/Main/System/Attribute/SimpleAttributeWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0179d1698f9b9d54c884f5d49a04f45a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Attribute/SimpleMinggeAttributeCell.cs b/Main/System/Attribute/SimpleMinggeAttributeCell.cs
new file mode 100644
index 0000000..8ba5ba3
--- /dev/null
+++ b/Main/System/Attribute/SimpleMinggeAttributeCell.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using UnityEngine;
+public class SimpleMinggeAttributeCell : MonoBehaviour
+{
+    [SerializeField] TextEx nameText;
+    [SerializeField] TextEx descText;
+    AttributeManager manager { get { return AttributeManager.Instance; } }
+    public void Display(int index, List<int> skillList, Dictionary<int, int> skillDic)
+    {
+        if (skillList.IsNullOrEmpty() || skillDic.IsNullOrEmpty() || index < 0 || index >= skillList.Count)
+            return;
+        int skillId = skillList[index];
+        if (!skillList.Contains(skillId))
+            return;
+        int lv = skillDic[skillId];
+        if (!manager.TryGetInfoBySkillID(skillId, lv, out string name, out string desc))
+            return;
+        nameText.text = name;
+        descText.text = desc;
+    }
+
+    public float GetHeight(string content)
+    {
+        var height = 0f;
+        if (string.IsNullOrEmpty(content))
+            return height;
+        descText.text = content;
+        return descText.preferredHeight + 5;
+    }
+}
\ No newline at end of file
diff --git a/Main/System/Attribute/SimpleMinggeAttributeCell.cs.meta b/Main/System/Attribute/SimpleMinggeAttributeCell.cs.meta
new file mode 100644
index 0000000..deea4b6
--- /dev/null
+++ b/Main/System/Attribute/SimpleMinggeAttributeCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a1aad0616b358bd4f8b709018b2d5540
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Attribute/SimpleMinggeAttributeWin.cs b/Main/System/Attribute/SimpleMinggeAttributeWin.cs
new file mode 100644
index 0000000..b490c48
--- /dev/null
+++ b/Main/System/Attribute/SimpleMinggeAttributeWin.cs
@@ -0,0 +1,101 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class SimpleMinggeAttributeWin : UIBase
+{
+    [SerializeField] TextEx title;
+    [SerializeField] ScrollerController scroller;
+    [SerializeField] ScrollerController skillScroller;
+    [SerializeField] SimpleMinggeAttributeCell simpleMinggeAttributeCell;
+    Dictionary<int, long> simpleAttrDict;
+    List<int> simpleAttrSortList;
+    string simpleAttrTitleName;
+    Dictionary<int, int> skillDic;
+    List<int> skillList;
+    protected override void OnPreOpen()
+    {
+        simpleAttrDict = AttributeManager.Instance.simpleAttrDict;
+        simpleAttrSortList = AttributeManager.Instance.simpleAttrSortList;
+        simpleAttrTitleName = AttributeManager.Instance.simpleAttrTitleName;
+        skillDic = AttributeManager.Instance.skillDic;
+        skillList = AttributeManager.Instance.skillList;
+
+        skillScroller.OnGetDynamicSize += OnGetSkillChatDynamicSize;
+        skillScroller.OnRefreshCell += OnRefreshSkillCell;
+
+        scroller.OnRefreshCell += OnRefreshCell;
+        title.text = simpleAttrTitleName;
+        CreateScroller();
+        CreateSkillScroller();
+    }
+
+    protected override void OnPreClose()
+    {
+        scroller.OnRefreshCell -= OnRefreshCell;
+
+        skillScroller.OnGetDynamicSize -= OnGetSkillChatDynamicSize;
+        skillScroller.OnRefreshCell -= OnRefreshSkillCell;
+    }
+
+    private void OnRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<SimpleAttributeAttrCell>();
+        _cell?.Display(cell.index, simpleAttrSortList, simpleAttrDict);
+    }
+
+    private void OnRefreshSkillCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell.GetComponent<SimpleMinggeAttributeCell>();
+        _cell?.Display(cell.index, skillList, skillDic);
+    }
+
+    private void CreateSkillScroller()
+    {
+        skillScroller.Refresh();
+        if (skillList != null)
+        {
+            for (int i = 0; i < skillList.Count; i++)
+            {
+                skillScroller.AddCell(ScrollerDataType.Header, i);
+            }
+        }
+        skillScroller.Restart();
+    }
+
+    private bool OnGetSkillChatDynamicSize(ScrollerDataType type, int index, out float height)
+    {
+        height = 0;
+        if (skillList.IsNullOrEmpty() || index < 0 || index >= skillList.Count)
+            return false;
+        if (!TryGetContentByIndex(index, out string content))
+            return false;
+        height = simpleMinggeAttributeCell.GetHeight(content);
+        return true;
+    }
+
+    private bool TryGetContentByIndex(int index, out string content)
+    {
+        content = string.Empty;
+        if (skillDic.IsNullOrEmpty() || index < 0 || index >= skillList.Count)
+            return false;
+        int skillID = skillList[index];
+        int lv = skillDic == null || !skillDic.ContainsKey(skillID) ? 0 : skillDic[skillID];
+        if (!AttributeManager.Instance.TryGetInfoBySkillID(skillID, lv, out string name, out content))
+            return false;
+        return true;
+    }
+
+    private void CreateScroller()
+    {
+        scroller.Refresh();
+        if (simpleAttrSortList != null)
+        {
+            int rowCount = (int)Mathf.Ceil((float)simpleAttrSortList.Count / AttributeManager.Instance.SimpleAttributeMaxRowCnt);
+            for (int i = 0; i < rowCount; i++)
+            {
+                scroller.AddCell(ScrollerDataType.Header, i);
+            }
+        }
+        scroller.Restart();
+    }
+}
\ No newline at end of file
diff --git a/Main/System/Attribute/SimpleMinggeAttributeWin.cs.meta b/Main/System/Attribute/SimpleMinggeAttributeWin.cs.meta
new file mode 100644
index 0000000..81828c7
--- /dev/null
+++ b/Main/System/Attribute/SimpleMinggeAttributeWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 10fbe3cb918521a4c97aac5716db4d4c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Battle/BattleConst.cs b/Main/System/Battle/BattleConst.cs
index c37db6f..0a8eeee 100644
--- a/Main/System/Battle/BattleConst.cs
+++ b/Main/System/Battle/BattleConst.cs
@@ -53,6 +53,11 @@
         {30030, WarlordPavilionBattleField},
     };
 
+    //pvp鎴樻枟鐨勬垬鍦猴紝涓嶅湪杩欎釜鍒楄〃鐨勮涓簆ve绫诲瀷鐨勬垬鍦�
+    public static List<string> pvpBattleNameDic = new List<string>()
+    {
+        ArenaBattleField,
+    };
 
     
     #endregion
diff --git a/Main/System/Battle/BattleManager.cs b/Main/System/Battle/BattleManager.cs
index 14bd87d..facc2d5 100644
--- a/Main/System/Battle/BattleManager.cs
+++ b/Main/System/Battle/BattleManager.cs
@@ -695,6 +695,12 @@
         return null;
     }
 
+    // 鍒ゆ柇鏄笉鏄疨vP绫诲瀷鐨勬垬鍦� true pvp false pve
+    public bool IsPvpBattle(string battleName)
+    {
+        return BattleConst.pvpBattleNameDic.Contains(battleName);
+    }
+
     public void DestroyAllBattleField()
     {
         var battleFieldsList = battleFields.Values.ToList();
diff --git a/Main/System/Battle/BattleObject/BattleObject.cs b/Main/System/Battle/BattleObject/BattleObject.cs
index ef76ebb..2c4ac20 100644
--- a/Main/System/Battle/BattleObject/BattleObject.cs
+++ b/Main/System/Battle/BattleObject/BattleObject.cs
@@ -99,6 +99,44 @@
         heroInfoBar = heroGo.GetComponentInChildren<BattleHeroInfoBar>(true);
         heroInfoBar.SetBattleObject(this);
 
+        //鐢ㄤ簬鐐瑰嚮姝﹀皢鏃惰Е鍙戣嚜瀹氫箟鍥炶皟
+        //涓荤嚎鍜屽ぉ瀛愮殑鎸戞垬浠ュ鐨勬垬鍦猴紝鐐瑰嚮姝﹀皢鍙互鏄剧ず姝﹀皢淇℃伅
+        if (battleField.ToString() != BattleConst.StoryBattleField &&
+            battleField.ToString() != BattleConst.TianziBillboradBattleField)
+        {
+            var hitArea = heroGo.GetComponent<Image>();
+            if (hitArea == null)
+            {
+                hitArea = heroGo.AddComponent<Image>();
+                hitArea.color = new Color(0, 0, 0, 0); //璁╁皠绾挎娴嬪埌
+            }
+
+            var btn = heroGo.GetComponent<ButtonEx>();
+            if (btn == null)
+            {
+                btn = heroGo.AddComponent<ButtonEx>();
+                btn.interval = 0.5f;// 闃叉棰戠箒杩炵画鐐瑰嚮
+            }
+
+            btn.AddListener(() =>
+            {
+                // 鍒ゆ柇鐐瑰嚮鐨勬槸鎴戞柟闃佃惀杩樻槸鏁屾柟闃佃惀锛岀洰鍓嶅乏杈归兘鏄垜鏂归樀钀�
+                bool isMySide = Camp == BattleCamp.Red;
+                EventBroadcast.Instance.Broadcast(EventName.BATTLE_CLICK_HERO, new BattleClickHeroData()
+                {
+                    battleName = battleField.ToString(),
+                    isMySide = isMySide,
+                    mapID = battleField.MapID,
+                    funcLineID = battleField.FuncLineID,
+                    npcID = teamHero.NPCID,
+                    posNum = teamHero.positionNum,
+                    heroID = teamHero.heroId,
+                    teams = battleField?.battleObjMgr.GetBattleObjList(isMySide ? BattleCamp.Red : BattleCamp.Blue),
+                });
+            });
+        }
+
+
         //  鏍规嵁闃佃惀缈昏浆琛�鏉�
         var heroInfoBarScale = heroInfoBar.transform.localScale;
         heroInfoBarScale.x *= Camp == BattleCamp.Red ? 1 : -1;
diff --git a/Main/System/BeautyMM/BeautyMMListWin.cs b/Main/System/BeautyMM/BeautyMMListWin.cs
index bfb021c..593753c 100644
--- a/Main/System/BeautyMM/BeautyMMListWin.cs
+++ b/Main/System/BeautyMM/BeautyMMListWin.cs
@@ -14,7 +14,7 @@
     {
         seeAttrBtn.AddListener(() =>
         {
-            AttributeManager.Instance.OpenTotalAttributeWin(BeautyMMManager.Instance.allMMTalentAttr);
+            AttributeManager.Instance.OpenSimpleAttributeWin(BeautyMMManager.Instance.allMMTalentAttr, "AttributeTitle01");
         });
         allMMBtn.AddListener(() =>
         {
diff --git a/Main/System/Gubao/GubaoListWin.cs b/Main/System/Gubao/GubaoListWin.cs
index 6239ea3..31fc57e 100644
--- a/Main/System/Gubao/GubaoListWin.cs
+++ b/Main/System/Gubao/GubaoListWin.cs
@@ -32,7 +32,7 @@
         });
         seeAttrBtn.AddListener(() =>
         {
-            AttributeManager.Instance.OpenTotalAttributeWin(GubaoManager.Instance.gubaoAllAttrDict);
+            AttributeManager.Instance.OpenSimpleAttributeWin(GubaoManager.Instance.gubaoAllAttrDict, "AttributeTitle02");
         });
     }
 
diff --git a/Main/System/Horse/HorseSkinWin.cs b/Main/System/Horse/HorseSkinWin.cs
index e43e639..10d1ec7 100644
--- a/Main/System/Horse/HorseSkinWin.cs
+++ b/Main/System/Horse/HorseSkinWin.cs
@@ -214,7 +214,7 @@
 
     void OnSeeAttrBtnClick()
     {
-        AttributeManager.Instance.OpenTotalAttributeWin(HorseManager.Instance.skinAttrDic);
+        AttributeManager.Instance.OpenSimpleAttributeWin(HorseManager.Instance.skinAttrDic, "AttributeTitle04");
     }
 
 
diff --git a/Main/System/Mingge/MinggeWin.cs b/Main/System/Mingge/MinggeWin.cs
index 2308b3d..9c3d6a6 100644
--- a/Main/System/Mingge/MinggeWin.cs
+++ b/Main/System/Mingge/MinggeWin.cs
@@ -41,7 +41,13 @@
         presetChooseCells.ChangeAlignment(TextAnchor.MiddleCenter);
         seeAttrBtn.AddListener(() =>
         {
-            AttributeManager.Instance.OpenTotalAttributeWin(MinggeManager.Instance.minggeAttrDict);
+            var skillTypeDict = MinggeManager.Instance.GetMinggeSkillTypeIDDict();
+            var skillDict = new Dictionary<int, int>();
+            foreach (var kvp in skillTypeDict)
+            {
+                skillDict[kvp.Key] = kvp.Value.Count;
+            }
+            AttributeManager.Instance.OpenMinggeSimpleAttributeWin(MinggeManager.Instance.minggeAttrDict, skillDict);
         });
         tyBtn.AddListener(SendTuiyan);
         for (int i = 0; i < suiteNameTexts.Length; i++)
diff --git a/Main/System/OtherPlayerDetail/OtherHeroDetailWin.cs b/Main/System/OtherPlayerDetail/OtherHeroDetailWin.cs
index 1431ce4..ae02354 100644
--- a/Main/System/OtherPlayerDetail/OtherHeroDetailWin.cs
+++ b/Main/System/OtherPlayerDetail/OtherHeroDetailWin.cs
@@ -6,6 +6,8 @@
 
 public class OtherHeroDetailWin : UIBase
 {
+    [SerializeField] ImageEx bgImage;
+    [SerializeField] ImageEx iconImage;
     [SerializeField] SkillBaseCell normalSkillCell;
     [SerializeField] SkillBaseCell angerSkillCell;
     [SerializeField] HeroShowBaseCell heroShow;
@@ -16,22 +18,25 @@
     [SerializeField] GameObject fetterGo;
     [SerializeField] Text[] fetterText;   //缇佺粖
     [SerializeField] Text[] fetterNameText;   //缇佺粖
+    [SerializeField] Color32 nameCol;
+    [SerializeField] Color32 descCol;
+    [SerializeField] Color32 grayCol;
 
     [SerializeField] GameObject potential;
     [SerializeField] GameObject potentialCell; //娼滆兘鍜岃閱掔敤浜庡垱寤�
     [SerializeField] Transform potentialCellParent; //娼滆兘鐖惰妭鐐�
     [SerializeField] Transform awakeCellParent; //娼滆兘鐖惰妭鐐�
     [SerializeField] GameObject awakeGo;
-
-    OtherPlayerDetailManager.RolePlusData.HeroData heroData;
-    List<OtherPlayerDetailManager.RolePlusData.HeroData> heroDatas;
     Dictionary<int, long> attrDict;
     OtherPlayerDetailManager manager { get { return OtherPlayerDetailManager.Instance; } }
     protected override void InitComponent()
     {
         awakeCellList = new List<GameObject>();
         potentialCellList = new List<GameObject>();
-        btnTotalAttr.SetListener(() => { AttributeManager.Instance.OpenTotalAttributeWin(attrDict); });
+        btnTotalAttr.SetListener(() =>
+        {
+            AttributeManager.Instance.OpenTotalAttributeWin(attrDict == null ? new Dictionary<int, long>() : attrDict);
+        });
     }
 
     protected override void OnPreOpen()
@@ -42,29 +47,149 @@
             allAttrScroll.verticalNormalizedPosition = 1f;
         }
 
-        heroData = manager.heroData;
-        heroDatas = manager.heroDatas;
-        attrDict = heroData.AttrDict;
+
+
         Display();
     }
 
     protected override void OnPreClose()
     {
 
+
+    }
+
+    private bool TryGetHeroInfo(BattleClickHeroData data, out HeroInfo heroInfo)
+    {
+        heroInfo = null;
+        if (!HeroManager.Instance.HasHero(data.heroID))
+            return false;
+        heroInfo = HeroManager.Instance.GetHeroByID(data.heroID);
+        return true;
     }
 
     private void Display()
     {
-        if (heroDatas.IsNullOrEmpty() || heroData == null)
+        attrDict = null;
+        switch (manager.viewHeroType)
         {
-            return;
+            case 0:
+                BattleClickHeroData data = manager.clickHeroData;
+                bgImage.SetSprite(data.isMySide ? "OtherHeroDetailBGBlue" : "OtherHeroDetailBGRed");
+                iconImage.SetSprite(data.isMySide ? "OtherHeroDetailIconBlue" : "OtherHeroDetailIconRed");
+                if (data.isMySide)
+                {
+                    if (!TryGetHeroInfo(data, out HeroInfo heroInfo))
+                    {
+                        SysNotifyMgr.Instance.ShowTip("NoHeroFromBag");
+                        DelayCloseWindow().Forget();
+                        return;
+                    }
+                    attrDict = FightPowerManager.Instance.GetHeroTotalAttr(heroInfo);
+                    DisplayByMyPack(heroInfo, data);
+                }
+                else
+                {
+                    if (BattleManager.Instance.IsPvpBattle(data.battleName))
+                    {
+                        attrDict = manager?.heroData?.AttrDict;
+                        DiplayByViewOtherPlayer();
+                    }
+                    else
+                    {
+                        attrDict = manager?.viewNPCAttr?.AttrDict;
+                        DisplayByViewNPCAttr();
+                    }
+
+                }
+                break;
+            case 1:
+            default:
+                bgImage.SetSprite("OtherHeroDetailBGRed");
+                iconImage.SetSprite("OtherHeroDetailIconRed");
+                attrDict = manager?.heroData?.AttrDict;
+                DiplayByViewOtherPlayer();
+                break;
         }
+    }
+
+    void DisplayByMyPack(HeroInfo heroInfo, BattleClickHeroData data)
+    {
+        if (heroInfo == null)
+            return;
+        HeroConfig heroConfig = heroInfo?.heroConfig;
+        if (heroConfig == null || data.teams == null || !data.isMySide)
+            return;
+
+        int heroID = heroConfig.HeroID;
+        int skinID = heroConfig?.SkinIDList[0] ?? 0;
+        int lv = heroInfo.heroLevel;
+        int star = heroInfo.heroStar;
+        int quality = heroConfig.Quality;
+        int breakLevel = heroInfo.breakLevel;
+        int awakeLevel = heroInfo.awakeLevel;
+        int atkSkillID = heroConfig.AtkSkillID;
+        int angerSkillID = heroConfig.AngerSkillID;
+        List<int> talentIDList = heroInfo.talentIDList;
+        List<int> talentLvList = heroInfo.talentLvList;
+        int[] FetterIDList = heroConfig.FetterIDList;
+
+        DisplaySkill(heroID, atkSkillID, angerSkillID);
+        DisplayHeroShow(heroID, skinID, breakLevel, star, awakeLevel, lv);
+        DisplayAttr(attrDict);
+        DisplayGiftBaseCell(heroID, awakeLevel, talentIDList, talentLvList);
+        DisplayFetter(data.teams, FetterIDList);
+        DisplayPotential(heroID, breakLevel);
+        RefreshAwake(heroID, quality, awakeLevel);
+
+        ForceRefreshLayout();
+
+    }
+
+    void DisplayByViewNPCAttr()
+    {
+        ViewNPCAttr viewNPCAttr = manager.viewNPCAttr;
+        if (viewNPCAttr == null)
+            return;
+
+        int heroID = (int)viewNPCAttr.HeroID;
+        if (!HeroConfig.HasKey(heroID))
+            return;
+
+        HeroConfig heroConfig = HeroConfig.Get(heroID);
+        int skinID = heroConfig?.SkinIDList[0] ?? 0;
+        int lv = viewNPCAttr.LV;
+        int star = viewNPCAttr.Star;
+        int quality = heroConfig.Quality;
+        int breakLevel = viewNPCAttr.BreakLV;
+        int awakeLevel = viewNPCAttr.AwakeLV;
+        int atkSkillID = heroConfig.AtkSkillID;
+        int angerSkillID = heroConfig.AngerSkillID;
+        List<int> talentIDList = null;
+        List<int> talentLvList = null;
+        int[] FetterIDList = heroConfig.FetterIDList;
+
+        DisplaySkill(heroID, atkSkillID, angerSkillID);
+        DisplayHeroShow(heroID, skinID, breakLevel, star, awakeLevel, lv);
+        DisplayAttr(attrDict);
+        DisplayGiftBaseCell(heroID, awakeLevel, talentIDList, talentLvList);
+        DisplayFetter(teams: null, FetterIDList);
+        DisplayPotential(heroID, breakLevel);
+        RefreshAwake(heroID, quality, awakeLevel);
+        ForceRefreshLayout();
+    }
+
+    void DiplayByViewOtherPlayer()
+    {
+
+        OtherPlayerDetailManager.RolePlusData.HeroData heroData = manager.heroData;
+        List<OtherPlayerDetailManager.RolePlusData.HeroData> heroDatas = manager.heroDatas;
+
+        if (heroDatas.IsNullOrEmpty() || heroData == null)
+            return;
 
         int heroID = heroData.HeroID;
         if (!HeroConfig.HasKey(heroID))
-        {
             return;
-        }
 
         HeroConfig heroConfig = HeroConfig.Get(heroID);
 
@@ -82,7 +207,7 @@
 
         DisplaySkill(heroID, atkSkillID, angerSkillID);
         DisplayHeroShow(heroID, skinID, breakLevel, star, awakeLevel, lv);
-        DisplayAttr(heroData);
+        DisplayAttr(attrDict);
         DisplayGiftBaseCell(heroID, awakeLevel, talentIDList, talentLvList);
         DisplayFetter(heroDatas, FetterIDList);
         DisplayPotential(heroID, breakLevel);
@@ -90,6 +215,7 @@
 
         ForceRefreshLayout();
     }
+
 
     List<GameObject> awakeCellList;   //瑙夐啋
     void RefreshAwake(int heroID, int quality, int awakeLevel)
@@ -164,7 +290,11 @@
     void DisplayPotential(int heroID, int breakLevel)
     {
         if (!HeroBreakConfig.configDics.ContainsKey(heroID))
+        {
+            potential.SetActive(false);
             return;
+        }
+
 
         var list = HeroBreakConfig.configDics[heroID].Keys.ToList();
 
@@ -218,31 +348,59 @@
             }
         }
     }
-
-    private void DisplayAttr(OtherPlayerDetailManager.RolePlusData.HeroData heroData)
+    private void DisplayAttr(Dictionary<int, long> attrDict)
     {
-        if (heroData == null || heroData.AttrDict == null)
-        {
+        if (attrDict.IsNullOrEmpty())
             return;
-        }
-        var attrDict = heroData.AttrDict;
         var baseAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.baseType];
         var fightAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.fightType];
-        var totalAttrIDList = baseAttrIDList.Concat(fightAttrIDList).ToList();
+        var antiAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.fightAntiType];
+        var totalAttrIDList = baseAttrIDList.Concat(fightAttrIDList).Concat(antiAttrIDList).ToList();
+
         for (int i = 0; i < attrs.Length; i++)
         {
             if (i < totalAttrIDList.Count)
             {
+
                 attrs[i].SetActive(true);
                 int attrId = totalAttrIDList[i];
-                long attrValue = attrDict.ContainsKey(attrId) ? attrDict[attrId] : 0;
-                attrs[i].text = PlayerPropertyConfig.GetFullDescription(attrId, attrValue);
+                if (PlayerPropertyConfig.HasKey(attrId))
+                {
+                    PlayerPropertyConfig config = PlayerPropertyConfig.Get(attrId);
+                    long attrValue = attrDict.ContainsKey(attrId) ? attrDict[attrId] : 0;
+                    attrs[i].text = Language.Get("OtherPlayerDetail15", config.Name, PlayerPropertyConfig.GetValueDescription(attrId, attrValue));
+                }
+                else
+                {
+                    attrs[i].SetActive(false);
+                }
+
             }
             else
             {
                 attrs[i].SetActive(false);
             }
         }
+    }
+
+    private bool HasFetterHero(List<BattleObject> teams, int heroID)
+    {
+        if (teams.IsNullOrEmpty())
+        {
+            return false;
+        }
+        for (int i = 0; i < teams.Count; i++)
+        {
+            if (teams[i].teamHero == null)
+            {
+                continue;
+            }
+            if (teams[i].teamHero.heroId == heroID)
+            {
+                return true;
+            }
+        }
+        return false;
     }
 
     private bool HasFetterHero(List<OtherPlayerDetailManager.RolePlusData.HeroData> heroDatas, int heroID)
@@ -259,6 +417,53 @@
             }
         }
         return false;
+    }
+
+    private void DisplayFetter(List<BattleObject> teams, int[] FetterIDList)
+    {
+        if (FetterIDList.IsNullOrEmpty() || teams.IsNullOrEmpty())
+        {
+            fetterGo.SetActive(false);
+            return;
+        }
+
+        for (int i = 0; i < fetterText.Length; i++)
+        {
+            if (i < FetterIDList.Length)
+            {
+                fetterText[i].SetActive(true);
+                var fetterID = FetterIDList[i];
+                HeroFetterConfig fetterConfig = HeroFetterConfig.Get(fetterID);
+                List<string> heroNames = new List<string>();
+                bool isAllCollect = true; //鏄惁鍏ㄦ敹闆�
+                foreach (var tmpHeroID in fetterConfig.HeroIDList)
+                {
+                    heroNames.Add(HeroConfig.Get(tmpHeroID).Name);
+                    bool hasFetterHero = HasFetterHero(teams, tmpHeroID);
+                    if (!hasFetterHero)
+                    {
+                        isAllCollect = false;
+                    }
+                }
+                fetterGo.SetActive(isAllCollect);
+                var attrStr = string.Join(Language.Get("SplitString1"), heroNames) + Language.Get("herocard38");
+
+                for (int j = 0; j < fetterConfig.AttrIDList.Length; j++)
+                {
+                    string format = !isAllCollect ? "{0}+{1}" : "{0}+" + UIHelper.AppendColor(TextColType.Green, "{1}");
+                    attrStr += Language.Get("L1112") + PlayerPropertyConfig.GetFullDescription(fetterConfig.AttrIDList[j], fetterConfig.AttrValueList[j], format);
+                }
+                fetterText[i].text = attrStr;
+                fetterText[i].color = isAllCollect ? descCol : grayCol;
+                fetterNameText[i].text = fetterConfig.FetterName;
+                fetterNameText[i].color = isAllCollect ? nameCol : grayCol;
+
+            }
+            else
+            {
+                fetterText[i].SetActive(false);
+            }
+        }
     }
 
     private void DisplayFetter(List<OtherPlayerDetailManager.RolePlusData.HeroData> heroDatas, int[] FetterIDList)
@@ -296,9 +501,9 @@
                     attrStr += Language.Get("L1112") + PlayerPropertyConfig.GetFullDescription(fetterConfig.AttrIDList[j], fetterConfig.AttrValueList[j], format);
                 }
                 fetterText[i].text = attrStr;
-                fetterText[i].color = UIHelper.GetUIColor(isAllCollect ? TextColType.NavyBrown : TextColType.NavyGray);
+                fetterText[i].color = isAllCollect ? descCol : grayCol;
                 fetterNameText[i].text = fetterConfig.FetterName;
-                fetterNameText[i].color = UIHelper.GetUIColor(isAllCollect ? TextColType.NavyBrown : TextColType.NavyGray);
+                fetterNameText[i].color = isAllCollect ? nameCol : grayCol;
 
             }
             else
@@ -310,12 +515,33 @@
 
     private void DisplayGiftBaseCell(int heroID, int awakeLevel, List<int> talentIDList, List<int> talentLvList)
     {
+        int showCount = HeroUIManager.Instance.GetGiftGirdMaxCount(heroID);
+
         if (talentIDList == null || talentLvList == null)
         {
+            for (int i = 0; i < giftBaseCells.Length; i++)
+            {
+                if (i >= showCount)
+                {
+                    giftBaseCells[i].SetActive(false);
+                    continue;
+                }
+
+                giftBaseCells[i].SetActive(true);
+                //闈炲姣旂殑鏄剧ず
+                if (i < HeroUIManager.Instance.normalGiftMaxCnt)
+                {
+                    giftBaseCells[i].Init(0, 0);
+                }
+                else
+                {
+                    giftBaseCells[i].Init(-1, 0, 0, heroID, i);
+                }
+            }
             return;
         }
 
-        int showCount = HeroUIManager.Instance.GetGiftGirdMaxCount(heroID);
+
         for (int i = 0; i < giftBaseCells.Length; i++)
         {
             if (i >= showCount)
@@ -354,6 +580,14 @@
 
     private void DisplaySkill(int heroID, int atkSkillID, int angerSkillID)
     {
+        if (!HeroConfig.HasKey(heroID) || !SkillConfig.HasKey(atkSkillID) || !SkillConfig.HasKey(angerSkillID))
+        {
+            normalSkillCell.SetActive(false);
+            angerSkillCell.SetActive(false);
+            return;
+        }
+        normalSkillCell.SetActive(true);
+        angerSkillCell.SetActive(true);
         normalSkillCell.Init(atkSkillID, () =>
         {
             UIManager.Instance.OpenWindow<HeroSkillWin>(heroID);
@@ -381,6 +615,8 @@
         {
             LayoutRebuilder.ForceRebuildLayoutImmediate(layout.GetComponent<RectTransform>());
         }
-
+        // Layout鍒锋柊瀹屾垚鍚庨噸鏂拌缃粴鍔ㄤ綅缃�
+        if (allAttrScroll != null)
+            allAttrScroll.verticalNormalizedPosition = 1f;
     }
 }
\ No newline at end of file
diff --git a/Main/System/OtherPlayerDetail/OtherHeroFightingCardItem.cs b/Main/System/OtherPlayerDetail/OtherHeroFightingCardItem.cs
index 5a89bc7..6168f4b 100644
--- a/Main/System/OtherPlayerDetail/OtherHeroFightingCardItem.cs
+++ b/Main/System/OtherPlayerDetail/OtherHeroFightingCardItem.cs
@@ -83,10 +83,9 @@
         {
             return;
         }
-        
+        manager.viewHeroType = 1;
         manager.heroData = heroData;
         manager.heroDatas = heros;
-
         if (!UIManager.Instance.IsOpened<OtherHeroDetailWin>())
         {
             UIManager.Instance.OpenWindow<OtherHeroDetailWin>();
diff --git a/Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs b/Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs
new file mode 100644
index 0000000..7d5bd9e
--- /dev/null
+++ b/Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs
@@ -0,0 +1,326 @@
+using System.Collections.Generic;
+using System.Linq;
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class OtherNPCDetailWin : UIBase
+{
+    [SerializeField] RectTransform funcarea;
+    [SerializeField] Transform bkImage1;
+    [SerializeField] Transform bkImage2;
+    [SerializeField] ImageEx bgImage;
+    [SerializeField] ImageEx iconImage;
+    [SerializeField] GameObject skill;
+    [SerializeField] SkillBaseCell normalSkillCell;
+    [SerializeField] SkillBaseCell angerSkillCell;
+    [SerializeField] OtherNpcHeroCell heroShow;
+    [SerializeField] ButtonEx btnTotalAttr;
+    [SerializeField] TextEx[] attrs;
+    [SerializeField] ScrollRect allAttrScroll;      //鍩瑰吇灞炴�� 婊氬姩鍖� 
+
+
+    [SerializeField] GameObject potential;
+    [SerializeField] GameObject potentialCell; //娼滆兘鍜岃閱掔敤浜庡垱寤�
+    [SerializeField] Transform potentialCellParent; //娼滆兘鐖惰妭鐐�
+    [SerializeField] Transform awakeCellParent; //娼滆兘鐖惰妭鐐�
+    [SerializeField] GameObject awakeGo;
+    Dictionary<int, long> attrDict;
+    OtherPlayerDetailManager manager { get { return OtherPlayerDetailManager.Instance; } }
+    protected override void InitComponent()
+    {
+        awakeCellList = new List<GameObject>();
+        potentialCellList = new List<GameObject>();
+        btnTotalAttr.SetListener(() =>
+        {
+            AttributeManager.Instance.OpenTotalAttributeWin(attrDict == null ? new Dictionary<int, long>() : attrDict);
+        });
+    }
+
+    protected override void OnPreOpen()
+    {
+        // 閲嶇疆鎵�鏈夋粴鍔ㄥ尯鍒伴《閮�
+        if (allAttrScroll != null)
+            allAttrScroll.verticalNormalizedPosition = 1f;
+
+        Display();
+    }
+
+    protected override void OnPreClose()
+    {
+
+
+    }
+    private bool IsSimple(ViewNPCAttr viewNPCAttr)
+    {
+        int heroID = (int)viewNPCAttr.HeroID;
+        return !HeroBreakConfig.configDics.ContainsKey(heroID) && !HeroAwakeConfig.CanAwake(heroID, 1);
+    }
+
+    private void Display()
+    {
+        BattleClickHeroData data = manager.clickHeroData;
+        bgImage.SetSprite(data.isMySide ? "OtherHeroDetailBGBlue" : "OtherHeroDetailBGRed");
+        iconImage.SetSprite(data.isMySide ? "OtherHeroDetailIconBlue" : "OtherHeroDetailIconRed");
+        attrDict = manager?.viewNPCAttr?.AttrDict;
+        DisplayByViewNPCAttr();
+    }
+
+    void DisplayByViewNPCAttr()
+    {
+        ViewNPCAttr viewNPCAttr = manager.viewNPCAttr;
+        if (viewNPCAttr == null)
+            return;
+
+        int heroID = (int)viewNPCAttr.HeroID;
+        if (!HeroConfig.HasKey(heroID))
+            return;
+
+        HeroConfig heroConfig = HeroConfig.Get(heroID);
+        bool isSimple = IsSimple(viewNPCAttr);
+        bkImage1.SetActive(!isSimple);
+        bkImage2.SetActive(isSimple);
+        funcarea.sizeDelta = new Vector2(funcarea.sizeDelta.x, isSimple ? 245 : 503);
+
+
+        int skinID = heroConfig?.SkinIDList[0] ?? 0;
+        int lv = viewNPCAttr.LV;
+        int star = viewNPCAttr.Star;
+        int quality = heroConfig.Quality;
+        int breakLevel = viewNPCAttr.BreakLV;
+        int awakeLevel = viewNPCAttr.AwakeLV;
+        int atkSkillID = heroConfig.AtkSkillID;
+        int angerSkillID = heroConfig.AngerSkillID;
+
+        DisplaySkill(heroID, atkSkillID, angerSkillID);
+        DisplayHeroShow(heroID, skinID, breakLevel, star, awakeLevel, lv);
+        DisplayAttr(attrDict);
+        DisplayPotential(heroID, breakLevel);
+        RefreshAwake(heroID, quality, awakeLevel);
+        ForceRefreshLayout();
+    }
+
+    List<GameObject> awakeCellList;   //瑙夐啋
+    void RefreshAwake(int heroID, int quality, int awakeLevel)
+    {
+
+        if (!HeroAwakeConfig.CanAwake(heroID, 1))
+        {
+            awakeGo.SetActive(false);
+            return;
+        }
+        awakeGo.SetActive(true);
+
+        var maxLV = HeroAwakeConfig.GetMaxAwakeLV(heroID);
+        var starCnt = HeroQualityConfig.Get(quality).InitStarUpper;
+        for (int i = 1; i <= maxLV; i++)
+        {
+            if (i > awakeCellList.Count)
+            {
+                awakeCellList.Add(Instantiate(potentialCell, awakeCellParent));
+            }
+            var go = awakeCellList[i - 1];
+            var descText = go.GetComponent<Text>();
+            var nameText = go.GetComponent<Text>("skillname");
+            go.SetActive(true);
+
+            var config = HeroAwakeConfig.GetHeroAwakeConfig(heroID, i);
+
+            int type = config.UnlockTalentSlot != 0 ? 1 : config.SkillID != 0 ? 2 : 3;
+            var awakeStr = string.Empty;
+            if (type == 1)
+            {
+                starCnt += config.AddStarUpper;
+                awakeStr = Language.Get("HeroAwake8", config.UnlockTalentSlot, starCnt);
+            }
+            else if (type == 2)
+            {
+                var skill = SkillConfig.Get(config.SkillID);
+                if (skill != null)
+                {
+                    awakeStr = Language.Get("L1039", skill.SkillName) + skill.Description;
+                }
+                else
+                {
+                    Debug.LogError($"heroId:{heroID} 瑙夐啋鎶�鑳絀D:{config.SkillID} 閰嶇疆閿欒鎶�鑳戒笉瀛樺湪");
+                }
+            }
+            else
+            {
+                for (int k = 0; k < config.AttrIDList.Length; k++)
+                {
+                    awakeStr += (string.IsNullOrEmpty(config.SkillIName) ? string.Empty : Language.Get("L1039", config.SkillIName)) +
+                    PlayerPropertyConfig.GetFullDescription(config.AttrIDList[k], config.AttrValueList[k], "{0}+" + UIHelper.AppendColor(TextColType.Green, "{1}"))
+                    + (k == config.AttrIDList.Length - 1 ? "" : "\n");
+                }
+            }
+
+            if (i - 1 < awakeLevel)
+            {
+                nameText.text = Language.Get("herocard12", i) + Language.Get("L1096");
+                descText.text = awakeStr;
+            }
+            else
+            {
+                //缃伆
+                nameText.text = UIHelper.AppendColor(TextColType.NavyGray, Language.Get("herocard12", i) + Language.Get("L1096"));
+                descText.text = UIHelper.AppendColor(TextColType.NavyGray, UIHelper.RemoveColor(awakeStr));
+            }
+        }
+    }
+
+
+
+    List<GameObject> potentialCellList;   //娼滆兘
+    void DisplayPotential(int heroID, int breakLevel)
+    {
+        if (!HeroBreakConfig.configDics.ContainsKey(heroID))
+        {
+            potential.SetActive(false);
+            return;
+        }
+
+
+        var list = HeroBreakConfig.configDics[heroID].Keys.ToList();
+
+        list.Sort();
+
+        for (int i = 0; i < list.Count; i++)
+        {
+            var nextQualityBreakConfig = HeroBreakConfig.GetHeroBreakConfig(heroID, i + 1);
+            if (nextQualityBreakConfig == null)
+                break;
+            List<string> attrStrArr = new List<string>();
+            for (int j = 0; j < nextQualityBreakConfig.AttrIDList.Length; j++)
+            {
+                if (nextQualityBreakConfig.AttrIDList[j] == 0)
+                    continue;
+                string format = i < breakLevel ? "{0}" + UIHelper.AppendColor(TextColType.Green, "+{1}") : "{0}+{1}";
+                attrStrArr.Add((string.IsNullOrEmpty(nextQualityBreakConfig.SkillIName) ? string.Empty : Language.Get("L1039", nextQualityBreakConfig.SkillIName)) +
+                PlayerPropertyConfig.GetFullDescription(nextQualityBreakConfig.AttrIDList[j], nextQualityBreakConfig.AttrValueList[j], format));
+            }
+
+            if (nextQualityBreakConfig.SkillID != 0)
+            {
+                var skill = SkillConfig.Get(nextQualityBreakConfig.SkillID);
+                if (skill != null)
+                {
+                    attrStrArr.Add(Language.Get("L1039", skill.SkillName) + skill.Description);
+                }
+                else
+                {
+                    Debug.LogError("鏈厤缃妧鑳�" + nextQualityBreakConfig.SkillID);
+                }
+            }
+            if (i >= potentialCellList.Count)
+            {
+                potentialCellList.Add(Instantiate(potentialCell, potentialCellParent));
+            }
+            var go = potentialCellList[i];
+            var descText = go.GetComponent<Text>();
+            var nameText = go.GetComponent<Text>("skillname");
+            go.SetActive(true);
+            if (i < breakLevel)
+            {
+                nameText.text = Language.Get("herocard63", i + 1);
+                descText.text = string.Join("\n", attrStrArr);
+            }
+            else
+            {
+                //缃伆
+                nameText.text = UIHelper.AppendColor(TextColType.NavyGray, Language.Get("herocard63", i + 1));
+                descText.text = UIHelper.AppendColor(TextColType.NavyGray, UIHelper.RemoveColor(string.Join("\n", attrStrArr)));
+            }
+        }
+    }
+    private void DisplayAttr(Dictionary<int, long> attrDict)
+    {
+        if (attrDict.IsNullOrEmpty())
+            return;
+        var baseAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.baseType];
+        var fightAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.fightType];
+        var antiAttrIDList = PlayerPropertyConfig.playerPropertyDict[PlayerPropertyConfig.fightAntiType];
+        var totalAttrIDList = baseAttrIDList.Concat(fightAttrIDList).Concat(antiAttrIDList).ToList();
+
+        for (int i = 0; i < attrs.Length; i++)
+        {
+            if (i < totalAttrIDList.Count)
+            {
+
+                attrs[i].SetActive(true);
+                int attrId = totalAttrIDList[i];
+                if (PlayerPropertyConfig.HasKey(attrId))
+                {
+                    PlayerPropertyConfig config = PlayerPropertyConfig.Get(attrId);
+                    long attrValue = attrDict.ContainsKey(attrId) ? attrDict[attrId] : 0;
+                    attrs[i].text = Language.Get("OtherPlayerDetail15", config.Name, PlayerPropertyConfig.GetValueDescription(attrId, attrValue));
+                }
+                else
+                {
+                    attrs[i].SetActive(false);
+                }
+
+            }
+            else
+            {
+                attrs[i].SetActive(false);
+            }
+        }
+    }
+
+    private void DisplayHeroShow(int heroID, int skinID, int breakLevel, int star, int awakeLevel, int lv)
+    {
+        heroShow.Init(heroID, skinID, breakLevel, star, awakeLevel, lv);
+    }
+
+    private void DisplaySkill(int heroID, int atkSkillID, int angerSkillID)
+    {
+        if (!HeroConfig.HasKey(heroID) || !SkillConfig.HasKey(atkSkillID) || !SkillConfig.HasKey(angerSkillID))
+        {
+            skill.SetActive(false);
+            return;
+        }
+        SkillConfig atkSkillIDConfig = SkillConfig.Get(atkSkillID);
+        SkillConfig angerSkillIDConfig = SkillConfig.Get(angerSkillID);
+
+        //娌℃湁鎶�鑳藉浘鏍囦篃涓嶆樉绀烘妧鑳�
+        if (string.IsNullOrEmpty(atkSkillIDConfig.IconName) || string.IsNullOrEmpty(angerSkillIDConfig.IconName))
+        {
+            skill.SetActive(false);
+            return;
+        }
+
+        skill.SetActive(true);
+        normalSkillCell.Init(atkSkillID, () =>
+        {
+            UIManager.Instance.OpenWindow<HeroSkillWin>(heroID);
+        }, true);
+        angerSkillCell.Init(angerSkillID, () =>
+        {
+            UIManager.Instance.OpenWindow<HeroSkillWin>(heroID);
+        }, true);
+    }
+
+    /// <summary>
+    /// 寮哄埗鍒锋柊Layout锛岃В鍐冲祵濂桳ayout鍜孋ontentSizeFitter鐨勯噸鍙犻棶棰�
+    /// </summary>
+    async UniTask ForceRefreshLayout()
+    {
+        await UniTask.DelayFrame(2);
+        // 鍒锋柊鎵�鏈塋ayout缁勪欢
+        var layouts = allAttrScroll.GetComponentsInChildren<LayoutGroup>(true);
+        foreach (var layout in layouts)
+        {
+            LayoutRebuilder.ForceRebuildLayoutImmediate(layout.GetComponent<RectTransform>());
+        }
+        await UniTask.DelayFrame(2);
+        // 鍒锋柊鎵�鏈塋ayout缁勪欢
+        foreach (var layout in layouts)
+        {
+            LayoutRebuilder.ForceRebuildLayoutImmediate(layout.GetComponent<RectTransform>());
+        }
+        // Layout鍒锋柊瀹屾垚鍚庨噸鏂拌缃粴鍔ㄤ綅缃�
+        if (allAttrScroll != null)
+            allAttrScroll.verticalNormalizedPosition = 1f;
+    }
+}
\ No newline at end of file
diff --git a/Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs.meta b/Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs.meta
new file mode 100644
index 0000000..0c8bd5c
--- /dev/null
+++ b/Main/System/OtherPlayerDetail/OtherNPCDetailWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d8178183658a6654cb57052375a17a33
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs b/Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs
new file mode 100644
index 0000000..aa7e674
--- /dev/null
+++ b/Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs
@@ -0,0 +1,210 @@
+锘縰sing UnityEngine;
+using UnityEngine.UI;
+using System.Collections.Generic;
+
+//姝﹀皢妯″瀷鏃犵珯鍙版ā鍧�
+public class OtherNpcHeroCell : MonoBehaviour
+{
+
+    UIHeroController m_HeroModel;
+    UIHeroController heroModel
+    {
+        get
+        {
+            if (m_HeroModel == null)
+            {
+                m_HeroModel = this.transform.GetComponent<UIHeroController>("Container_HeroShow/rolemodel");
+            }
+            return m_HeroModel;
+        }
+    }
+
+    Transform m_StarRect;
+    Transform starRect
+    {
+        get
+        {
+            if (m_StarRect == null)
+            {
+                m_StarRect = this.transform.Find("Container_HeroShow/layout/layout/stars");
+            }
+            return m_StarRect;
+        }
+    }
+
+    List<Image> m_StarsImg;
+    List<Image> starsImg
+    {
+        get
+        {
+            if (m_StarsImg == null)
+            {
+                m_StarsImg = new List<Image>();
+                for (int i = 0; i < 5; i++)
+                {
+                    var star = this.transform.GetComponent<Image>("Container_HeroShow/layout/layout/stars/star" + i);
+                    m_StarsImg.Add(star);
+                }
+            }
+            return m_StarsImg;
+        }
+    }
+
+
+    Image m_LockState;
+    Image lockState
+    {
+        get
+        {
+            if (m_LockState == null)
+            {
+                m_LockState = this.transform.GetComponent<Image>("Container_HeroShow/lockstate");
+            }
+            return m_LockState;
+        }
+    }
+
+    Text m_Name;
+    Text nameText
+    {
+        get
+        {
+            if (m_Name == null)
+            {
+                m_Name = this.transform.GetComponent<Text>("Container_HeroShow/layout/name");
+            }
+            return m_Name;
+        }
+    }
+
+
+    Text m_LvText;  //姝﹀皢绛夌骇 + 瑙夐啋绛夌骇
+    Text lvText
+    {
+        get
+        {
+            if (m_LvText == null)
+            {
+                m_LvText = this.transform.GetComponent<Text>("Container_HeroShow/layout/layout/lv");
+            }
+            return m_LvText;
+        }
+    }
+
+    OutlineEx m_LvTextOutline;
+    OutlineEx heroLVOutline
+    {
+        get
+        {
+            if (m_LvTextOutline == null)
+            {
+                m_LvTextOutline = this.transform.GetComponent<OutlineEx>("Container_HeroShow/layout/layout/lv");
+            }
+            return m_LvTextOutline;
+        }
+    }
+
+    Button m_StarBtn;
+    Button starBtn
+    {
+        get
+        {
+            if (m_StarBtn == null)
+            {
+                m_StarBtn = this.transform.GetComponent<Button>("Container_HeroShow/layout/layout/stars");
+            }
+            return m_StarBtn;
+        }
+    }
+
+
+    void Awake()
+    {
+        LoadPrefab();
+    }
+
+    // 姝﹀皢妯″瀷绔欏彴鏄剧ず
+    public void Init(int heroID, int skinID, int breakLevel = 0, int star = 0, int awakelv = 0, int lv = 0, bool isShowLock = false)
+    {
+        LoadPrefab();   //瀛樺湪琚嵏杞界殑鍙兘锛岄噸鏂板姞杞�
+        var heroConfig = HeroConfig.Get(heroID);
+
+        heroModel.Create(skinID, 1.2f);
+
+        if (star == 0)
+        {
+            starRect.SetActive(false);
+        }
+        else
+        {
+            starRect.SetActive(true);
+            for (int i = 0; i < starsImg.Count; i++)
+            {
+                if ((star - 1) % starsImg.Count >= i)
+                {
+                    starsImg[i].SetActive(true);
+                    starsImg[i].SetSprite("herostar" + (((star - 1) / starsImg.Count) + 1) * starsImg.Count);
+                }
+                else
+                {
+                    starsImg[i].SetActive(false);
+                }
+            }
+        }
+
+        lockState.SetActive(isShowLock);
+
+        nameText.text = breakLevel == 0 ? heroConfig.Name : Language.Get("herocardbreaklv", heroConfig.Name, breakLevel);
+        nameText.color = UIHelper.GetUIColorByFunc(heroConfig.Quality);
+        if (lv == 0)
+        {
+            lvText.SetActive(false);
+        }
+        else
+        {
+            lvText.SetActive(true);
+            if (awakelv == 0)
+            {
+                lvText.text = string.Format("{0}{1}", Language.Get("L1094"), lv);
+            }
+            else
+            { 
+                lvText.text = string.Format("{0}{1} {2}", Language.Get("L1094"), lv, Language.Get("herocard12", awakelv));
+            }
+            heroLVOutline.colorType = awakelv == 0 ? QualityTextColType.None : QualityTextColType.red;
+        }
+
+        starBtn.AddListener(() =>
+        { 
+            SmallTipWin.showText = Language.Get("HeroGift14", star);
+            SmallTipWin.worldPos = CameraManager.uiCamera.ScreenToWorldPoint(Input.mousePosition);
+            UIManager.Instance.OpenWindow<SmallTipWin>();
+        });
+    }
+
+    GameObject cellContainer;
+    protected void LoadPrefab()
+    {
+        if (cellContainer != null)
+            return;
+
+        var tmp = transform.Find("Container_HeroShow");
+        if (tmp != null)
+        {
+            cellContainer = tmp.gameObject;
+            return;
+        }
+        if (cellContainer == null)
+        {
+            cellContainer = UIUtility.CreateWidget("OtherNpcHeroCell", "Container_HeroShow");
+
+            if (cellContainer != null)
+            {
+                cellContainer.transform.SetParentEx(this.transform, Vector3.zero, Quaternion.identity, Vector3.one);
+                cellContainer.transform.SetAsFirstSibling();
+            }
+        }
+
+    }
+}
+
diff --git a/Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs.meta b/Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs.meta
new file mode 100644
index 0000000..f66edf3
--- /dev/null
+++ b/Main/System/OtherPlayerDetail/OtherNpcHeroCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a237531ac4d059148a7e71e9a8e1a4f8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/OtherPlayerDetail/OtherPlayerDetailManager.cs b/Main/System/OtherPlayerDetail/OtherPlayerDetailManager.cs
index 7b5fadb..94fae49 100644
--- a/Main/System/OtherPlayerDetail/OtherPlayerDetailManager.cs
+++ b/Main/System/OtherPlayerDetail/OtherPlayerDetailManager.cs
@@ -8,6 +8,26 @@
 //鏌ョ湅鍏朵粬鐜╁鐨勭畝鐭俊鎭�, 璇ユā鍧楀鐞嗘暟鎹紝鍏朵粬鐢卞悇鑷姛鑳芥ā鍧楀鐞� 閫氳繃OnRevPackage
 public class OtherPlayerDetailManager : GameSystemManager<OtherPlayerDetailManager>
 {
+    public int viewPlayer { get; private set; }
+
+    // 鏌ヨ绫诲瀷锛氭牴鎹被鍨嬪仛涓嶅悓澶勭悊
+    // 鎸夊姛鑳借嚜瀹氫箟鏋氫妇鍊� EnumHelper鐨� ViewPlayerType
+    public int viewPlayerType { get; private set; }
+
+    public int viewPreSetType { get; private set; }
+
+    private Dictionary<int, ViewPlayerData> viewPlayerDataDic = new Dictionary<int, ViewPlayerData>();
+
+    // 鏌ョ湅绫诲瀷锛岀帺瀹禝D
+    public event Action<int, int> OnRevPackage; //灏介噺涓嶈鐢ㄧ涓�涓弬鏁皏iewtype鍋氬垽鏂紝瀹规槗鍑洪敊
+
+
+    public int viewHeroType;//0:鎴樺満鐐瑰嚮妯″瀷 1锛氭煡鐪嬩粬浜鸿嫳闆勮鎯呯晫闈㈢偣鍑昏嫳闆勫崱鐗�
+    public BattleClickHeroData clickHeroData;
+    public ViewNPCAttr viewNPCAttr;
+    public RolePlusData.HeroData heroData;
+    public List<RolePlusData.HeroData> heroDatas;
+    public RolePlusData.EquipData equip;
 
     public override void Init()
     {
@@ -22,54 +42,39 @@
 
     public void OnBeforePlayerDataInitializeEventOnRelogin()
     {
+        viewPlayer = -1;
         viewPlayerType = -1;
-        viewPlayerLineupType = -1;
+        viewPreSetType = -1;
     }
 
-    public int viewPlayer { get; private set; }
-
-    // 鏌ヨ绫诲瀷锛氭牴鎹被鍨嬪仛涓嶅悓澶勭悊
-    // 鎸夊姛鑳借嚜瀹氫箟鏋氫妇鍊� EnumHelper鐨� ViewPlayerType
-    public int viewPlayerType { get; private set; }
-
-    public int viewPlayerLineupType { get; private set; }
-    public RolePlusData.HeroData heroData;
-    public List<RolePlusData.HeroData> heroDatas;
-    public RolePlusData.EquipData equip;
-    private Dictionary<int, ViewPlayerData> viewPlayerDataDic = new Dictionary<int, ViewPlayerData>();
-
-    // 鏌ョ湅绫诲瀷锛岀帺瀹禝D
-    public event Action<int, int> OnRevPackage; //灏介噺涓嶈鐢ㄧ涓�涓弬鏁皏iewtype鍋氬垽鏂紝瀹规槗鍑洪敊
-
     // 鑾峰彇鍏朵粬鐜╁鏈湴缂撳瓨鏁版嵁
-    public ViewPlayerData GetViewPlayerData(int player)
+    public ViewPlayerData GetViewPlayerData(int playerId)
     {
         ViewPlayerData viewPlayerData = null;
-        viewPlayerDataDic.TryGetValue(player, out viewPlayerData);
+        viewPlayerDataDic.TryGetValue(playerId, out viewPlayerData);
         return viewPlayerData;
     }
 
-
     // 鍚戞湇鍔$璇锋眰鍏朵粬鐜╁鏁版嵁
-    public void ViewPlayerDetail(int _playerId, int viewType = (int)ViewPlayerType.viewPlayerData, int viewPlayerLineupType = (int)BattlePreSetType.Story)
+    public void ViewPlayerDetail(int playerId, int viewType = (int)ViewPlayerType.viewPlayerData, int viewBattlePreSetType = (int)BattlePreSetType.Story)
     {
-        if (_playerId == PlayerDatas.Instance.baseData.PlayerID)
-        {
+        // 鑷繁涓嶈兘鏌ョ湅鑷繁鐨勪俊鎭�
+        if (playerId == PlayerDatas.Instance.baseData.PlayerID)
             return;
-        }
+
         viewPlayerType = viewType;
-        this.viewPlayerLineupType = viewPlayerLineupType;
-        ViewRoleParticulars(_playerId);
+        viewPreSetType = viewBattlePreSetType;
+        ViewRoleParticulars(playerId);
     }
 
     // 鍚戞湇鍔$璇锋眰鐜╁鏁版嵁
     void ViewRoleParticulars(int playerID)
     {
-        if (playerID == 0)
-        {
+        if (playerID <= 0)
             return;
-        }
+
         viewPlayer = playerID;
+
         if (viewPlayerDataDic.ContainsKey(playerID))
         {
             ViewPlayerData viewPlayerData = viewPlayerDataDic[playerID];
@@ -80,34 +85,10 @@
             }
         }
 
-        //CC002_tagCGViewCrossPlayerInfo c002 = new CC002_tagCGViewCrossPlayerInfo();
-
         CA212_tagCMViewPlayerInfo pak = new CA212_tagCMViewPlayerInfo();
         pak.PlayerID = (uint)playerID;
         GameNetSystem.Instance.SendInfo(pak);
     }
-
-
-    //鏈湇绔炴妧鍦哄亣鏌ヨ妯℃嫙灏佸寘锛岃繑鍥炴煡鐪嬫満鍣ㄤ汉鏁版嵁 鎵撳紑RoleParticularsWin鐣岄潰锛屼笓鐢ㄦ帴鍙e叾浠栧湴鏂逛笉瑕佽皟鐢�
-    public void ViewFairyArenaRobot(int playerID, ViewPlayerData _viewPlayerData)
-    {
-        ViewPlayerData viewPlayerData = null;
-        viewPlayer = playerID;
-        if (!viewPlayerDataDic.TryGetValue(playerID, out viewPlayerData))
-        {
-            viewPlayerDataDic.Add(playerID, _viewPlayerData);
-        }
-        else
-        {
-            viewPlayerDataDic[playerID] = _viewPlayerData;
-        }
-
-        viewPlayerType = (int)ViewPlayerType.viewPlayerData;
-        ShowRoleParticulars(playerID);
-    }
-
-
-
     public void OnRevRoleEquip(HA705_tagSCQueryPlayerCacheResult package)
     {
 
@@ -159,15 +140,46 @@
         {
             if (!UIManager.Instance.IsOpened<OtherPlayerDetailWin>())
             {
-                UIManager.Instance.OpenWindow<OtherPlayerDetailWin>(viewPlayerLineupType);
+                UIManager.Instance.OpenWindow<OtherPlayerDetailWin>(viewPreSetType);
             }
         }
 
         OnRevPackage?.Invoke(viewPlayerType, playerID);
         viewPlayerType = -1;
-        viewPlayerLineupType = -1;
+        viewPreSetType = -1;
     }
+
     #region 鑾峰彇PlusData涓殑鏁版嵁
+
+    /// <summary>
+    /// 鑾峰彇鐗瑰畾鎴樻枟鐜涓嬬殑鍛芥牸鏂规璇︽儏
+    /// </summary>
+    public RolePlusData.MinggePresetData GetMinggePresetByBattleType(int playerID, int battleType)
+    {
+        int presetID = GetFuncPresetID(playerID, battleType, (int)FuncPresetType.Mingge);
+        var minggeData = GetMinggeTotalData(playerID);
+        if (minggeData == null || minggeData.PresetDic == null)
+            return null;
+
+        // 鍙栧嚭瀵瑰簲鐨勬柟妗堟暟鎹�
+        if (minggeData.PresetDic.TryGetValue(presetID, out var data))
+        {
+            return data;
+        }
+
+        // 濡傛灉鎵句笉鍒拌鏂规ID锛堝吋瀹规�э級锛屽皾璇曞彇榛樿鏂规 ID=1
+        if (presetID != 1 && minggeData.PresetDic.TryGetValue(1, out var defaultData))
+        {
+            return defaultData;
+        }
+
+        return null;
+    }
+    public RolePlusData.MinggeData GetMinggeTotalData(int playerID)
+    {
+        ViewPlayerData viewPlayerData = GetViewPlayerData(playerID);
+        return viewPlayerData?.rolePlusData?.minggeData;
+    }
 
     public int GetFuncPresetID(int playerID, int battleType, int funcType)
     {
@@ -362,6 +374,13 @@
         return viewPlayerData?.rolePlusData?.horseData;
     }
 
+    // 鑾峰彇鍙ゅ疂鏁版嵁瀵硅薄
+    public RolePlusData.GubaoData GetGubaoData(int playerID)
+    {
+        ViewPlayerData viewPlayerData = GetViewPlayerData(playerID);
+        return viewPlayerData?.rolePlusData?.gubaoData;
+    }
+
     #endregion
     #region 瑙f瀽PlusData
     public class ViewPlayerData
@@ -395,6 +414,8 @@
 
         // 闃靛瀛楀吀 <闃靛ID, 闃靛鏁版嵁>
         public Dictionary<int, LineupData> LineupDic = new Dictionary<int, LineupData>();
+
+        //<鎴樻枟棰勮绫诲瀷,<棰勮绫诲瀷,浣跨敤鐨勬柟妗圛D>>
         public Dictionary<int, Dictionary<int, int>> BatPresetDic = new Dictionary<int, Dictionary<int, int>>();
 
         // 绾㈤鏁版嵁
@@ -402,6 +423,12 @@
 
         // 鍧愰獞鏁版嵁
         public HorseData horseData;
+
+        // 鍙ゅ疂鏁版嵁
+        public GubaoData gubaoData;
+
+        // 鍛芥牸鏁版嵁
+        public MinggeData minggeData;
 
         // 绾㈤鏁版嵁绫�
         public class BeautyData
@@ -423,6 +450,27 @@
         {
             public int ItemID;
             public Dictionary<int, List<int>> UserData = new Dictionary<int, List<int>>();
+        }
+
+        // 鍙ゅ疂鏁版嵁绫�
+        public class GubaoData
+        {
+            public int Cnt; // 宸茶В閿佹暟
+            public Dictionary<int, long> AttrDict = new Dictionary<int, long>(); // 灞炴��
+        }
+
+        public class MinggeData
+        {
+            public int GWLV; // 鎰熸偀绛夌骇
+            public Dictionary<int, MinggePresetData> PresetDic = new Dictionary<int, MinggePresetData>(); // <棰勮ID, 棰勮璇︽儏>
+        }
+
+        public class MinggePresetData
+        {
+            // 鍛芥牸鎶�鑳� <鎰忚薄鎶�鑳絀D, 绛夌骇>
+            public Dictionary<int, int> SkillDic = new Dictionary<int, int>();
+            // 鍛芥牸灞炴�� <灞炴�D, 鍊�>
+            public Dictionary<int, long> AttrDict = new Dictionary<int, long>();
         }
 
         // 闃靛鏁版嵁绫�
@@ -490,6 +538,9 @@
                 BatPresetDic.Clear();
                 beautyData = null; // 蹇呴』缃┖锛岄槻姝㈡樉绀轰笂涓�涓帺瀹剁殑鏁版嵁
                 horseData = null;  // 蹇呴』缃┖
+                gubaoData = null;  // 蹇呴』缃┖
+                minggeData = null;
+
                 // 1. 杞负 JsonData 瀵硅薄
                 JsonData jd = JsonMapper.ToObject(jsonStr);
 
@@ -514,6 +565,20 @@
                         }
                     }
                 }
+
+                // 銆愭柊澧炪�戣В鏋愬彜瀹� (Gubao)
+                if (jd.Keys.Contains("Gubao"))
+                {
+                    JsonData gubaoJd = jd["Gubao"];
+                    gubaoData = new GubaoData();
+
+                    if (gubaoJd.Keys.Contains("Cnt"))
+                        gubaoData.Cnt = (int)gubaoJd["Cnt"];
+
+                    // 瑙f瀽灞炴��
+                    gubaoData.AttrDict = ParseAttrDict(gubaoJd, "Attr");
+                }
+
                 // 銆愭柊澧炪�戣В鏋愮孩棰� (Beauty)
                 if (jd.Keys.Contains("Beauty"))
                 {
@@ -624,6 +689,52 @@
                     }
 
                 }
+                // 銆愭柊澧炪�戣В鏋愬懡鏍� (Mingge)
+                if (jd.Keys.Contains("Mingge"))
+                {
+                    JsonData minggeJd = jd["Mingge"];
+                    minggeData = new MinggeData();
+
+                    // 1. 瑙f瀽鎰熸偀绛夌骇
+                    if (minggeJd.Keys.Contains("GWLV"))
+                        minggeData.GWLV = (int)minggeJd["GWLV"];
+
+                    // 2. 瑙f瀽棰勮 (Preset)
+                    if (minggeJd.Keys.Contains("Preset"))
+                    {
+                        JsonData presetRootJd = minggeJd["Preset"];
+                        foreach (string key in presetRootJd.Keys)
+                        {
+                            // key 涓哄懡鏍奸璁綢D
+                            if (int.TryParse(key, out int presetId))
+                            {
+                                JsonData singlePresetJd = presetRootJd[key];
+                                MinggePresetData presetData = new MinggePresetData();
+
+                                // 瑙f瀽灞炴�� (Attr)
+                                presetData.AttrDict = ParseAttrDict(singlePresetJd, "Attr");
+
+                                // 瑙f瀽鎶�鑳� (Skill) - {"SkillID": level, ...}
+                                if (singlePresetJd.Keys.Contains("Skill"))
+                                {
+                                    JsonData skillJd = singlePresetJd["Skill"];
+                                    foreach (string skillKey in skillJd.Keys)
+                                    {
+                                        if (int.TryParse(skillKey, out int skillId))
+                                        {
+                                            if (int.TryParse(skillJd[skillKey].ToString(), out int level))
+                                            {
+                                                presetData.SkillDic[skillId] = level;
+                                            }
+                                        }
+                                    }
+                                }
+
+                                minggeData.PresetDic[presetId] = presetData;
+                            }
+                        }
+                    }
+                }
 
             }
             catch (Exception e)
diff --git a/Main/System/OtherPlayerDetail/OtherPlayerDetailWin.cs b/Main/System/OtherPlayerDetail/OtherPlayerDetailWin.cs
index 109e112..86fc9e7 100644
--- a/Main/System/OtherPlayerDetail/OtherPlayerDetailWin.cs
+++ b/Main/System/OtherPlayerDetail/OtherPlayerDetailWin.cs
@@ -28,6 +28,10 @@
     [SerializeField] ButtonEx btnReport;
     [SerializeField] ButtonEx btnAddFriend;
     [SerializeField] ButtonEx btnPrivateChat;
+    [SerializeField] ButtonEx btnMingge;
+    [SerializeField] TextEx txtMinggeLv;
+    [SerializeField] ButtonEx btnGubao;
+    [SerializeField] TextEx txtGubaoLv;
     OtherPlayerDetailManager.ViewPlayerData viewPlayerData;
     OtherPlayerDetailManager manager { get { return OtherPlayerDetailManager.Instance; } }
 
@@ -40,12 +44,14 @@
         btnGuild.SetListener(OnClickGuild);
         btnMM.SetListener(OnClickMM);
         btnHorse.SetListener(OnClickHorse);
+        btnMingge.SetListener(OnClickMingge);
+        btnGubao.SetListener(OnClickGubao);
     }
 
     protected override void OnPreOpen()
     {
         GuildManager.Instance.OnRefreshFairyList += OnRefreshFairyList;
-        funcPresetID = OtherPlayerDetailManager.Instance.GetFuncPresetID(manager.viewPlayer, functionOrder, (int)FuncPresetType.Team);
+        funcPresetID = OtherPlayerDetailManager.Instance.GetFuncPresetID(manager.viewPlayer, functionOrder, (int)FuncPresetType.Global);
         Display();
     }
 
@@ -89,7 +95,6 @@
         int playerID = viewPlayerData.PlayerID;
 
         DisplayMM(playerID);
-
         var heroList = manager.GetHeroDataSortList(playerID, funcPresetID);
         if (heroList.IsNullOrEmpty())
         {
@@ -110,13 +115,29 @@
 
         var equipDict = manager.GetEquipDataDict(playerID);
         DisplayEquip(equipDict);
+
+        DisplayGubao(playerID);
+        DisplayMingge(playerID);
     }
 
     private void DisplayMM(int playerID)
     {
         OtherPlayerDetailManager.RolePlusData.BeautyData beautyData = manager.GetBeautyData(playerID);
-        txtMMCnt.text = Language.Get("OtherPlayerDetail08", beautyData == null ? 0 : beautyData.Cnt);
+        txtMMCnt.text = Language.Get("OtherPlayerDetail11", beautyData == null ? 0 : beautyData.Cnt);
     }
+
+    private void DisplayGubao(int playerID)
+    {
+        OtherPlayerDetailManager.RolePlusData.GubaoData gubaoData = manager.GetGubaoData(playerID);
+        txtGubaoLv.text = Language.Get("OtherPlayerDetail12", gubaoData == null ? 0 : gubaoData.Cnt);
+    }
+
+    private void DisplayMingge(int playerID)
+    {
+        var mingge = manager.GetMinggeTotalData(playerID);
+        txtMinggeLv.text = Language.Get("OtherPlayerDetail13", mingge == null ? 0 : mingge.GWLV);
+    }
+
     private void DisplayGuildInfo(OtherPlayerDetailManager.ViewPlayerData viewPlayerData)
     {
         if (viewPlayerData.FamilyEmblemID <= 0 || string.IsNullOrEmpty(viewPlayerData.FamilyEmblemWord))
@@ -259,7 +280,7 @@
         int playerID = viewPlayerData.PlayerID;
         OtherPlayerDetailManager.RolePlusData.BeautyData beautyData = manager.GetBeautyData(playerID);
 
-        AttributeManager.Instance.OpenTotalAttributeWin(beautyData == null || beautyData.AttrDict == null ? new Dictionary<int, long>() : beautyData.AttrDict);
+        AttributeManager.Instance.OpenSimpleAttributeWin(beautyData == null || beautyData.AttrDict == null ? new Dictionary<int, long>() : beautyData.AttrDict, "AttributeTitle01");
     }
     private void OnClickHorse()
     {
@@ -273,6 +294,31 @@
         {
             return;
         }
-        AttributeManager.Instance.OpenTotalAttributeWin(horseData.AttrDict == null ? new Dictionary<int, long>() : horseData.AttrDict);
+        AttributeManager.Instance.OpenSimpleAttributeWin(horseData.AttrDict == null ? new Dictionary<int, long>() : horseData.AttrDict, "AttributeTitle04");
+    }
+
+    private void OnClickMingge()
+    {
+        if (viewPlayerData == null)
+            return;
+        int playerID = viewPlayerData.PlayerID;
+
+        // 鏍规嵁褰撳墠鍏ュ彛鐨勬垬鏂楅璁剧被鍨嬭幏鍙栧懡鏍奸璁炬暟鎹�
+        var minggePresetData = manager.GetMinggePresetByBattleType(playerID, functionOrder);
+
+        // 鑾峰彇灞炴�у瓧鍏革紝濡傛灉涓虹┖鍒欎娇鐢ㄧ┖瀛楀吀
+        var attrDict = minggePresetData?.AttrDict ?? new Dictionary<int, long>();
+        var skillDict = minggePresetData?.SkillDic ?? new Dictionary<int, int>();
+        // 鎵撳紑灞炴�х獥鍙�
+        AttributeManager.Instance.OpenMinggeSimpleAttributeWin(attrDict, skillDict);
+    }
+
+    private void OnClickGubao()
+    {
+        if (viewPlayerData == null)
+            return;
+        int playerID = viewPlayerData.PlayerID;
+        OtherPlayerDetailManager.RolePlusData.GubaoData gubaoData = manager.GetGubaoData(playerID);
+        AttributeManager.Instance.OpenSimpleAttributeWin(gubaoData == null || gubaoData.AttrDict == null ? new Dictionary<int, long>() : gubaoData.AttrDict, "AttributeTitle02");
     }
 }
diff --git a/Main/System/PhantasmPavilion/PhantasmPavilionFaceWin.cs b/Main/System/PhantasmPavilion/PhantasmPavilionFaceWin.cs
index 6e49784..3963e99 100644
--- a/Main/System/PhantasmPavilion/PhantasmPavilionFaceWin.cs
+++ b/Main/System/PhantasmPavilion/PhantasmPavilionFaceWin.cs
@@ -69,7 +69,7 @@
         btnFace.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Face, true); });
         btnFacePic.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.FacePic, true); });
         btnChatBox.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.ChatBox, true); });
-        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenTotalAttributeWin(manager.GetTotalAttr()); });
+        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenSimpleAttributeWin(manager.GetTotalAttr()); });
     }
 
     protected override void OnPreOpen()
diff --git a/Main/System/PhantasmPavilion/PhantasmPavilionModelWin.cs b/Main/System/PhantasmPavilion/PhantasmPavilionModelWin.cs
index 580589a..6cc20c6 100644
--- a/Main/System/PhantasmPavilion/PhantasmPavilionModelWin.cs
+++ b/Main/System/PhantasmPavilion/PhantasmPavilionModelWin.cs
@@ -45,7 +45,7 @@
         base.InitComponent();
         btnTabType1.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Model, 0, true); });
         btnTabType2.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Model, 1, true); });
-        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenTotalAttributeWin(manager.GetTotalAttr()); });
+        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenSimpleAttributeWin(manager.GetTotalAttr()); });
     }
 
     protected override void OnPreOpen()
diff --git a/Main/System/PhantasmPavilion/PhantasmPavilionTilteWin.cs b/Main/System/PhantasmPavilion/PhantasmPavilionTilteWin.cs
index 05ff8f6..f976df1 100644
--- a/Main/System/PhantasmPavilion/PhantasmPavilionTilteWin.cs
+++ b/Main/System/PhantasmPavilion/PhantasmPavilionTilteWin.cs
@@ -51,7 +51,7 @@
         btnTakeOff3.AddListener(() => { manager.SendOPPack(PhantasmPavilionType.Title, PhantasmPavilionOperation.Remove, (uint)manager.selectId); });
         btnTabType1.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Title, 0); });
         btnTabType2.AddListener(() => { SelectTeamFunc(PhantasmPavilionType.Title, 1); });
-        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenTotalAttributeWin(manager.GetTotalAttr()); });
+        btnAllAdd.AddListener(() => { AttributeManager.Instance.OpenSimpleAttributeWin(manager.GetTotalAttr()); });
     }
 
     protected override void OnPreOpen()
diff --git a/Main/System/ViewNPC/ViewNPCManager.cs b/Main/System/ViewNPC/ViewNPCManager.cs
index 8412697..f6a8b42 100644
--- a/Main/System/ViewNPC/ViewNPCManager.cs
+++ b/Main/System/ViewNPC/ViewNPCManager.cs
@@ -5,21 +5,145 @@
 
 public class ViewNPCManager : GameSystemManager<ViewNPCManager>
 {
+    // 缂撳瓨瀛楀吀缁撴瀯: MapID -> FuncLineID -> NPCID -> Attr
     public Dictionary<uint, Dictionary<uint, Dictionary<uint, ViewNPCAttr>>> dict = new Dictionary<uint, Dictionary<uint, Dictionary<uint, ViewNPCAttr>>>();
+    private BattleClickHeroData pendingData; // 鐢ㄤ簬璁板綍褰撳墠姝e湪璇锋眰鏌ョ湅鐨� NPC 淇℃伅锛屼互渚垮洖鍖呮椂鑷姩鎵撳紑鐣岄潰 ---
+    private bool isPendingOpen = false; // 鏄惁鏄偣鍑昏Е鍙戠殑璇锋眰锛堥渶瑕佽嚜鍔ㄦ墦寮�鐣岄潰锛�
+    OtherPlayerDetailManager otherPlayerManager { get { return OtherPlayerDetailManager.Instance; } }
     public override void Init()
     {
         DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
+        EventBroadcast.Instance.AddListener<BattleClickHeroData>(EventName.BATTLE_CLICK_HERO, OnBattleClickHero);
+        OtherPlayerDetailManager.Instance.OnRevPackage += OnRevPackage;
     }
 
     public override void Release()
     {
         DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
+        EventBroadcast.Instance.RemoveListener<BattleClickHeroData>(EventName.BATTLE_CLICK_HERO, OnBattleClickHero);
+        OtherPlayerDetailManager.Instance.OnRevPackage -= OnRevPackage;
     }
+
 
     private void OnBeforePlayerDataInitializeEventOnRelogin()
     {
         dict.Clear();
+        ResetPendingState();
     }
+
+    private void ResetPendingState()
+    {
+        isPendingOpen = false;
+        pendingData.isMySide = false;
+        pendingData.funcLineID = 0;
+        pendingData.mapID = 0;
+        pendingData.npcID = 0;
+        pendingData.teams = null;
+    }
+
+    private void OnBattleClickHero(BattleClickHeroData data)
+    {
+        pendingData = data;
+        // 涓荤嚎涓嶆樉绀篘PC淇℃伅
+        if (data.mapID == 1)
+            return;
+
+        // 鑷繁鐨勬暟鎹洿鎺ヨ鑳屽寘
+        if (data.isMySide)
+        {
+            OpenNPCDetailWin(pendingData, null);
+            return;
+        }
+
+        // pvp鐨勮蛋鏌ョ湅鐜╁鐨�
+        if (BattleManager.Instance.IsPvpBattle(data.battleName))
+        {
+            switch (data.battleName)
+            {
+                case BattleConst.ArenaBattleField:
+                    int playerId = (int)ArenaManager.Instance.atkPlayerId;
+                    OtherPlayerDetailManager.Instance.ViewPlayerDetail(playerId, (int)ViewPlayerType.viewArenaBattleEnemyHero, (int)BattlePreSetType.Arena);
+                    break;
+            }
+            return;
+        }
+        // 闈瀙vp鐨勮蛋鏌ョ湅npc鐨�
+        // 灏濊瘯鐩存帴浠庣紦瀛樿幏鍙栨暟鎹�
+        if (TryGetNPCAttr((uint)data.mapID, (uint)data.funcLineID, (uint)data.npcID, out ViewNPCAttr attr))
+        {
+            OpenNPCDetailWin(pendingData, attr);
+        }
+        else
+        {
+            isPendingOpen = true;
+            SendViewNPCAttr(data);
+        }
+    }
+
+    private void OnRevPackage(int viewPlayerType, int playerID)
+    {
+        switch (viewPlayerType)
+        {
+            case (int)ViewPlayerType.viewArenaBattleEnemyHero:
+                ViewArenaBattleEnemyHero(playerID);
+                break;
+        }
+    }
+
+    private void ViewArenaBattleEnemyHero(int playerID)
+    {
+        int presetID = otherPlayerManager.GetFuncPresetID(playerID, OtherPlayerDetailManager.Instance.viewPreSetType, (int)FuncPresetType.Global);
+        var heroList = otherPlayerManager.GetHeroDataSortList(playerID, presetID);
+        if (heroList.IsNullOrEmpty())
+        {
+            heroList = otherPlayerManager.GetHeroDataSortList(playerID, FuncPresetManager.FuncDefaultPresetID);
+        }
+        var heroData = otherPlayerManager.GetHeroDataDict(playerID, presetID);
+        if (heroList.IsNullOrEmpty())
+        {
+            heroList = otherPlayerManager.GetHeroDataSortList(playerID, FuncPresetManager.FuncDefaultPresetID);
+        }
+        if (heroList == null || heroData == null)
+            return;
+        int posNum = pendingData.posNum + 1;
+        if (!heroData.ContainsKey(posNum))
+            return;
+        OtherPlayerDetailManager.Instance.viewHeroType = 0;
+        OtherPlayerDetailManager.Instance.clickHeroData = pendingData;
+        OtherPlayerDetailManager.Instance.heroData = heroData[posNum];
+        OtherPlayerDetailManager.Instance.heroDatas = heroList;
+
+        if (!UIManager.Instance.IsOpened<OtherHeroDetailWin>())
+        {
+            UIManager.Instance.OpenWindow<OtherHeroDetailWin>();
+        }
+    }
+
+
+    private void OpenNPCDetailWin(BattleClickHeroData data, ViewNPCAttr npcAttr)
+    {
+        OtherPlayerDetailManager.Instance.viewHeroType = 0;
+        OtherPlayerDetailManager.Instance.clickHeroData = data;
+        OtherPlayerDetailManager.Instance.viewNPCAttr = npcAttr;
+
+        if (data.isMySide)
+        {
+            if (!UIManager.Instance.IsOpened<OtherHeroDetailWin>())
+            {
+                UIManager.Instance.OpenWindow<OtherHeroDetailWin>();
+            }
+        }
+        else
+        {
+            if (!UIManager.Instance.IsOpened<OtherNPCDetailWin>())
+            {
+                UIManager.Instance.OpenWindow<OtherNPCDetailWin>();
+            }
+        }
+
+
+    }
+
     // --- 瑙f瀽灞炴�у瓧鍏� ---
     private Dictionary<int, long> ParseAttrDict(string jsonStr)
     {
@@ -34,18 +158,12 @@
                 if (int.TryParse(attrKey, out int attrId))
                 {
                     JsonData v = jd[attrKey];
-                    long val = 0;
-                    // 鍋ュ.鎬ф暟鍊艰浆鎹�
-                    if (v.IsLong) val = (long)v;
-                    else if (v.IsInt) val = (long)(int)v;
-                    else if (v.IsDouble) val = (long)(double)v;
-                    else if (v.IsString) long.TryParse(v.ToString(), out val);
-
+                    long.TryParse(v.ToString(), out long val);
                     result[attrId] = val;
                 }
             }
         }
-        catch (System.Exception e)
+        catch (Exception e)
         {
             Debug.LogWarning($"瑙f瀽NPC灞炴�SON澶辫触: {e.Message}");
         }
@@ -53,8 +171,10 @@
     }
 
     public event Action<uint, uint> OnUpdateViewNPCAttrRet;
+
     public void UpdateViewNPCAttrRet(HB432_tagSCViewNPCAttrRet vNetData)
     {
+        // 1. 鏇存柊瀛楀吀鏁版嵁
         if (!dict.ContainsKey(vNetData.MapID))
         {
             dict[vNetData.MapID] = new Dictionary<uint, Dictionary<uint, ViewNPCAttr>>();
@@ -102,8 +222,27 @@
                 };
             }
         }
+
+        // 妫�鏌ユ槸鍚﹂渶瑕佽嚜鍔ㄦ墦寮�鐣岄潰 (Pending 閫昏緫)
+        if (isPendingOpen)
+        {
+            // 妫�鏌ュ洖鍖呮槸鍚﹀搴斿綋鍓嶇殑璇锋眰 (MapID 鍜� FuncLineID 鍖归厤)
+            if (vNetData.MapID == pendingData.mapID && vNetData.FuncLineID == pendingData.funcLineID)
+            {
+                if (TryGetNPCAttr((uint)pendingData.mapID, (uint)pendingData.funcLineID, (uint)pendingData.npcID, out ViewNPCAttr targetAttr))
+                {
+                    OpenNPCDetailWin(pendingData, targetAttr);
+                }
+                // 澶勭悊瀹屾瘯锛岄噸缃姸鎬�
+                ResetPendingState();
+            }
+            return;
+        }
+
         OnUpdateViewNPCAttrRet?.Invoke(vNetData.MapID, vNetData.FuncLineID);
+
     }
+
 
 
     public bool TryGetNPCAttr(uint mapID, uint funcLineID, uint npcID, out ViewNPCAttr npcAttr)
@@ -121,6 +260,11 @@
         }
 
         return funcLineDict.TryGetValue(npcID, out npcAttr);
+    }
+
+    public void SendViewNPCAttr(BattleClickHeroData data)
+    {
+        SendViewNPCAttr((uint)data.mapID, (uint)data.funcLineID, (uint)data.npcID);
     }
 
     public void SendViewNPCAttr(uint mapID, uint funcLineID, uint viewNPCID)
@@ -143,4 +287,16 @@
     public byte BreakLV;        // 绐佺牬
     public byte AwakeLV;        // 瑙夐啋
     public Dictionary<int, long> AttrDict = new Dictionary<int, long>(); // 灞炴��;
+}
+
+public struct BattleClickHeroData
+{
+    public string battleName;
+    public bool isMySide;
+    public int heroID;
+    public int funcLineID;
+    public int mapID;
+    public int npcID;
+    public int posNum;
+    public List<BattleObject> teams;
 }
\ No newline at end of file
diff --git a/Main/System/WarlordPavilion/WarlordPavilionWin.cs b/Main/System/WarlordPavilion/WarlordPavilionWin.cs
index 3ec5ac1..66a0d5f 100644
--- a/Main/System/WarlordPavilion/WarlordPavilionWin.cs
+++ b/Main/System/WarlordPavilion/WarlordPavilionWin.cs
@@ -91,7 +91,6 @@
     protected override void OnPreOpen()
     {
         GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
-        ViewNPCManager.Instance.OnUpdateViewNPCAttrRet += OnUpdateViewNPCAttrRet;
         manager.OnUpdateDingjungeInfoEvent += OnUpdateDingjungeInfo;
         Display();
     }
@@ -99,7 +98,6 @@
     protected override void OnPreClose()
     {
         GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
-        ViewNPCManager.Instance.OnUpdateViewNPCAttrRet -= OnUpdateViewNPCAttrRet;
         manager.OnUpdateDingjungeInfoEvent -= OnUpdateDingjungeInfo;
     }
 
diff --git a/Main/Utility/EnumHelper.cs b/Main/Utility/EnumHelper.cs
index a6159c1..87756a9 100644
--- a/Main/Utility/EnumHelper.cs
+++ b/Main/Utility/EnumHelper.cs
@@ -1841,5 +1841,5 @@
 {
     viewPlayerData = 0,  //鏌ョ湅鐜╁鍩烘湰淇℃伅锛屽叕鐢ㄦ墦寮�鐣岄潰
     viewGuildLeader = 1,  //鏌ョ湅鐜╁鐨勫叕浼氭棌闀夸俊鎭�
-
+    viewArenaBattleEnemyHero = 2,  //鏌ョ湅绔炴妧鍦烘垬鏂楁晫鏂硅嫳闆勪俊鎭�
 }

--
Gitblit v1.8.0