From 3575c016271e455cfaab964b98e3d4ef84ef75cb Mon Sep 17 00:00:00 2001
From: lcy <1459594991@qq.com>
Date: 星期三, 13 五月 2026 20:06:12 +0800
Subject: [PATCH] 597 时装特卖

---
 Main/Config/Configs/ActSpecialSaleConfig.cs                      |   32 ++
 Main/Utility/EnumHelper.cs                                       |    1 
 Main/System/Store/SkinStoreCell.cs                               |    2 
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs      |   18 +
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs.meta       |   11 
 Main/System/Store/SkinStoreLineCell.cs                           |    2 
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs.meta   |   11 
 Main/System/Redpoint/MainRedDot.cs                               |    1 
 Main/System/Store/SkinStoreWin.cs                                |    9 
 Main/System/HeroSkinFlashSale.meta                               |    8 
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs           |   45 +++
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs        |  242 +++++++++++++++++
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs        |   33 ++
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs.meta |   11 
 Main/System/HeroUI/HeroSkinWin.cs                                |   67 ++++
 Main/System/Main/HomeWin.cs                                      |   20 +
 Main/System/UIBase/UIJumpManager.cs                              |   24 +
 Main/System/OpenServerActivity/OperationTimeHepler.cs            |    4 
 Main/Main.cs                                                     |    2 
 Main/Config/Configs/StoreConfig.cs                               |    8 
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs.meta      |   11 
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs.meta   |   11 
 Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs            |  229 ++++++++++++++++
 Main/System/Store/StoreModel.cs                                  |   29 ++
 24 files changed, 818 insertions(+), 13 deletions(-)

diff --git a/Main/Config/Configs/ActSpecialSaleConfig.cs b/Main/Config/Configs/ActSpecialSaleConfig.cs
index 7415d3c..8ab72d1 100644
--- a/Main/Config/Configs/ActSpecialSaleConfig.cs
+++ b/Main/Config/Configs/ActSpecialSaleConfig.cs
@@ -1,6 +1,6 @@
 锘�//--------------------------------------------------------
 //    [Author]:           YYL
-//    [  Date ]:           2026骞�4鏈�29鏃�
+//    [  Date ]:           Monday, May 11, 2026
 //--------------------------------------------------------
 
 using System.Collections.Generic;
@@ -21,6 +21,8 @@
 	public int ActShopType;
 	public int ADID;
 	public int[][] PopItems;
+	public float[] OriginalPrices;
+	public float[] PercentageTexts;
 
     public override int LoadKey(string _key)
     {
@@ -53,6 +55,34 @@
 			int.TryParse(tables[3],out ADID); 
 
 			PopItems = JsonMapper.ToObject<int[][]>(tables[4].Replace("(", "[").Replace(")", "]")); 
+
+			if (tables[5].Contains("["))
+			{
+				OriginalPrices = JsonMapper.ToObject<float[]>(tables[5]);
+			}
+			else
+			{
+				string[] OriginalPricesStringArray = tables[5].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
+				OriginalPrices = new float[OriginalPricesStringArray.Length];
+				for (int i=0;i<OriginalPricesStringArray.Length;i++)
+				{
+					 float.TryParse(OriginalPricesStringArray[i],out OriginalPrices[i]);
+				}
+			}
+
+			if (tables[6].Contains("["))
+			{
+				PercentageTexts = JsonMapper.ToObject<float[]>(tables[6]);
+			}
+			else
+			{
+				string[] PercentageTextsStringArray = tables[6].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
+				PercentageTexts = new float[PercentageTextsStringArray.Length];
+				for (int i=0;i<PercentageTextsStringArray.Length;i++)
+				{
+					 float.TryParse(PercentageTextsStringArray[i],out PercentageTexts[i]);
+				}
+			}
         }
         catch (Exception exception)
         {
diff --git a/Main/Config/Configs/StoreConfig.cs b/Main/Config/Configs/StoreConfig.cs
index d8938f5..464c7d6 100644
--- a/Main/Config/Configs/StoreConfig.cs
+++ b/Main/Config/Configs/StoreConfig.cs
@@ -1,6 +1,6 @@
 锘�//--------------------------------------------------------
 //    [Author]:           YYL
-//    [  Date ]:           2026骞�3鏈�5鏃�
+//    [  Date ]:           2026骞�5鏈�9鏃�
 //--------------------------------------------------------
 
 using System.Collections.Generic;
@@ -32,6 +32,8 @@
 	public int UnlockValue;
 	public int IsExclusive;
 	public string Name;
+	public int StartTime;
+	public int EndTime;
 
     public override int LoadKey(string _key)
     {
@@ -74,6 +76,10 @@
 			int.TryParse(tables[14],out IsExclusive); 
 
 			Name = tables[15];
+
+			int.TryParse(tables[16],out StartTime); 
+
+			int.TryParse(tables[17],out EndTime); 
         }
         catch (Exception exception)
         {
diff --git a/Main/Main.cs b/Main/Main.cs
index f2c56e5..8b851a0 100644
--- a/Main/Main.cs
+++ b/Main/Main.cs
@@ -123,7 +123,7 @@
         managers.Add(FestivalActivityCheckInManager.Instance);
         managers.Add(FestivalActivityMissionManager.Instance);
         managers.Add(SuperVipManager.Instance);
-
+        managers.Add(HeroSkinFlashSaleManager.Instance);
 
         foreach (var manager in managers)
         {
diff --git a/Main/System/HeroSkinFlashSale.meta b/Main/System/HeroSkinFlashSale.meta
new file mode 100644
index 0000000..290e03d
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c64d4c964e9ce144f93fe33877c7a7e2
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs
new file mode 100644
index 0000000..8bd9c0b
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs
@@ -0,0 +1,18 @@
+using UnityEngine;
+
+public class HeroSkinFlashSaleAwardCell : CellView
+{
+    [SerializeField] ItemCell itemCell;
+    public void Display(int index, int[][] arr)
+    {
+        if (arr == null || arr.Length <= index) return;
+        int itemID = arr[index][0];
+        long itemCount = arr[index][1];
+        
+        itemCell.Init(new ItemCellModel(itemID, false, itemCount));
+        itemCell.button.AddListener(() =>
+        {
+            ItemTipUtility.Show(itemID);
+        });
+    }
+}
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs.meta b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs.meta
new file mode 100644
index 0000000..3375ce8
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleAwardCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d2bb86c6193afa54a8e94eecd3e431ea
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs
new file mode 100644
index 0000000..e6eb72d
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs
@@ -0,0 +1,45 @@
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+public class HeroSkinFlashSaleCell : MonoBehaviour
+{
+    [SerializeField] ButtonEx clickButton;
+    [SerializeField] ImageEx bgImage;
+    [SerializeField] ImageEx heroImage;
+    HeroSkinFlashSaleManager manager => HeroSkinFlashSaleManager.Instance;
+    public void Display()
+    {
+        clickButton.SetListener(() =>
+        {
+            UIManager.Instance.OpenWindow<HeroSkinFlashSaleWin>();
+        });
+
+        var act = manager.GetActInfo();
+        if (act == null) return;
+
+        var config = ActSpecialSaleConfig.Get(act.CfgID);
+        if (config == null) return;
+
+        var list = manager.GetSkinIDList(act.CfgID);
+        if (list == null) return;
+
+        int skinID = list[0];
+        var skinConfig = HeroSkinConfig.Get(skinID);
+        if (skinConfig == null) return;
+
+        bgImage.SetSprite(StringUtility.Concat("HeroSkinFlashSaleEntryBg_", skinID.ToString()));
+        SetHeroSquareIcon(heroImage, skinConfig.SquareIcon).Forget();
+    }
+
+    async UniTaskVoid SetHeroSquareIcon(ImageEx image, string name)
+    {
+        var sprite = UILoader.LoadSprite("HeroHead", name);
+        if (sprite != null)
+        {
+            image.overrideSprite = sprite;
+            return;
+        }
+
+        image.SetSprite("herohead_default");
+    }
+
+}
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs.meta b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs.meta
new file mode 100644
index 0000000..e6b8b9a
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 86088ecd589cb704fab2ef2f87e31949
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs
new file mode 100644
index 0000000..5feedbe
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Collections.Generic;
+
+public class HeroSkinFlashSaleManager : GameSystemManager<HeroSkinFlashSaleManager>, IOpenServerActivity
+{
+    public override void Init()
+    {
+        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
+        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk;
+        OperationTimeHepler.Instance.operationTimeUpdateEvent += OperationTimeUpdateEvent;
+        OperationTimeHepler.Instance.operationStartEvent += OperationStartEvent;
+        OperationTimeHepler.Instance.operationEndEvent += OperationEndEvent;
+        OperationTimeHepler.Instance.operationAdvanceEvent += OperationAdvanceEvent;
+    }
+
+    public override void Release()
+    {
+        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
+        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk;
+        OperationTimeHepler.Instance.operationTimeUpdateEvent -= OperationTimeUpdateEvent;
+        OperationTimeHepler.Instance.operationStartEvent -= OperationStartEvent;
+        OperationTimeHepler.Instance.operationEndEvent -= OperationEndEvent;
+        OperationTimeHepler.Instance.operationAdvanceEvent -= OperationAdvanceEvent;
+    }
+
+    private void OnBeforePlayerDataInitializeEventOnRelogin()
+    {
+
+    }
+
+    private void OnPlayerLoginOk()
+    {
+    }
+
+    public const int activityID = (int)OperationType.HeroSkinFlashSale;
+    public static OperationType operaType = OperationType.HeroSkinFlashSale;
+    public Redpoint redPoint = new Redpoint(MainRedDot.HeroSkinFlashSaleRepoint);
+    public bool IsOpen => OperationTimeHepler.Instance.SatisfyOpenCondition(operaType);
+    public bool IsAdvance => OperationTimeHepler.Instance.SatisfyAdvanceCondition(operaType);
+    public bool priorityOpen => redPoint.state == RedPointState.Simple;
+    public readonly int actNum = 10;
+    public event Action<int> onStateUpdate;
+
+    private void OperationTimeUpdateEvent(OperationType type)
+    {
+        if (type == operaType)
+        {
+            if (UIManager.Instance.IsOpened<HeroSkinFlashSaleWin>())
+                UIManager.Instance.CloseWindow<HeroSkinFlashSaleWin>();
+        }
+    }
+
+    private void OperationStartEvent(OperationType type, int state)
+    {
+        if (type == operaType && state == 0)
+        {
+            onStateUpdate?.Invoke(activityID);
+        }
+    }
+
+    private void OperationEndEvent(OperationType type, int state)
+    {
+        if (type == operaType)
+        {
+            if (UIManager.Instance.IsOpened<HeroSkinFlashSaleWin>())
+                UIManager.Instance.CloseWindow<HeroSkinFlashSaleWin>();
+            onStateUpdate?.Invoke(activityID);
+        }
+    }
+
+    private void OperationAdvanceEvent(OperationType type)
+    {
+        if (type == operaType)
+        {
+            onStateUpdate?.Invoke(activityID);
+        }
+    }
+    public bool IsHeroSkinFlashSaleOpen()
+    {
+        if (!IsOpen) return false;
+        if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.HeroSkinFlashSale)) return false;
+        return true;
+    }
+
+    public OperationFlashSaleActivityInfo GetActInfo()
+    {
+        OperationTimeHepler.Instance.TryGetOperation(operaType, out OperationFlashSaleActivityInfo act);
+        return act;
+    }
+
+    public void GetActTimeStr(TextEx timeText, string key = "TimeRush05")
+    {
+        var act = GetActInfo();
+        if (act == null)
+        {
+            timeText.text = Language.Get("OSActivity6");
+            return;
+        }
+        timeText.text = Language.Get(key, TimeUtility.SecondsToShortDHMS(act.GetResetSurplusTime()));
+    }
+
+
+    public int GetDefaultSkinID(int heroID)
+    {
+        HeroConfig heroConfig = HeroConfig.Get(heroID);
+        if (heroConfig == null || heroConfig.SkinIDList.IsNullOrEmpty()) return 0;
+        return heroConfig.SkinIDList[0];
+    }
+
+    public bool IsNoSellOutCTGID(int ctgID)
+    {
+        CTGConfig config = CTGConfig.Get(ctgID);
+        if (config == null) return false;
+        if (!RechargeManager.Instance.TryGetRechargeCount(ctgID, out var rechargeCount)) return false;
+        return rechargeCount.totalCount < config.TotalBuyCount;
+    }
+
+    public event Action OnCurrentChooseSkinIDChangeEevent;
+    private int m_currentChooseSkinID = 0;
+
+    public int currentChooseSkinID
+    {
+        get
+        {
+            return m_currentChooseSkinID;
+        }
+        set
+        {
+            if (m_currentChooseSkinID == value) return;
+            m_currentChooseSkinID = value;
+            OnCurrentChooseSkinIDChangeEevent?.Invoke();
+        }
+    }
+
+    /// <summary>
+    /// 閫氳繃skinID鑾峰彇瀵瑰簲鐨刪eroID
+    /// </summary>
+    public int GetHeroIDBySkinID(int skinID)
+    {
+        foreach (var heroConfig in HeroConfig.GetValues())
+        {
+            if (heroConfig.SkinIDList != null && heroConfig.SkinIDList.Contains(skinID))
+            {
+                return heroConfig.HeroID;
+            }
+        }
+        return 0;
+    }
+
+    /// <summary>
+    /// 鑾峰彇skinID鍦℉eroConfig.SkinIDList涓殑绱㈠紩
+    /// </summary>
+    public int GetSkinIndexInHeroConfig(int heroID, int skinID)
+    {
+        var heroConfig = HeroConfig.Get(heroID);
+        if (heroConfig?.SkinIDList == null) return int.MaxValue;
+
+        for (int i = 0; i < heroConfig.SkinIDList.Length; i++)
+        {
+            if (heroConfig.SkinIDList[i] == skinID)
+                return i;
+        }
+        return int.MaxValue;
+    }
+
+    //<skinID,ctgID>
+    Dictionary<int, int> ctgDict = new();
+    public int GetCtgIDBySkinID(int skinID)
+    {
+        if (ctgDict.IsNullOrEmpty())
+        {
+            GetSkinIDToCtgIDDict();
+        }
+
+        int ctgID;
+        ctgDict.TryGetValue(skinID, out ctgID);
+        return ctgID;
+    }
+
+    Dictionary<int, int> GetSkinIDToCtgIDDict()
+    {
+        if (!ctgDict.IsNullOrEmpty())
+        {
+            return ctgDict;
+        }
+
+        foreach (var config in ActSpecialSaleConfig.GetValues())
+        {
+            if (config == null || config.CTGIDList == null) return null;
+
+            for (int i = 0; i < config.CTGIDList.Length; i++)
+            {
+                var ctgID = config.CTGIDList[i];
+                var ctgConfig = CTGConfig.Get(ctgID);
+                if (ctgConfig == null || ctgConfig.GainItemList == null) continue;
+
+                for (int j = 0; j < ctgConfig.GainItemList.Length; j++)
+                {
+                    var itemID = ctgConfig.GainItemList[j][0];
+                    var itemConfig = ItemConfig.Get(itemID);
+
+                    if (itemConfig == null) continue;
+                    if (!HeroSkinAttrConfig.TryGetSkinIDByItemID(itemID, out var skinID)) continue;
+                    if (ctgDict.ContainsKey(skinID)) continue;
+                    ctgDict[skinID] = ctgID;
+                }
+            }
+        }
+
+        return ctgDict;
+    }
+
+
+    public List<int> GetSkinIDList(int cfgID)
+    {
+        var config = ActSpecialSaleConfig.Get(cfgID);
+        if (config == null || config.CTGIDList == null) return null;
+
+        var res = new List<int>();
+        for (int i = 0; i < config.CTGIDList.Length; i++)
+        {
+            var ctgID = config.CTGIDList[i];
+            var ctgConfig = CTGConfig.Get(ctgID);
+            if (ctgConfig == null || ctgConfig.GainItemList == null) continue;
+
+            for (int j = 0; j < ctgConfig.GainItemList.Length; j++)
+            {
+                var itemID = ctgConfig.GainItemList[j][0];
+                var itemConfig = ItemConfig.Get(itemID);
+
+                if (itemConfig == null) continue;
+                if (!HeroSkinAttrConfig.TryGetSkinIDByItemID(itemID, out var skinID)) continue;
+                if (res.Contains(skinID)) continue;
+
+                res.Add(skinID);
+            }
+        }
+        return res;
+    }
+
+}
+
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs.meta b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs.meta
new file mode 100644
index 0000000..c238dea
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 950599ca1ed881243991a8e394b1a0d3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs
new file mode 100644
index 0000000..5b2a880
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class HeroSkinFlashSaleTabCell : CellView
+{
+    [SerializeField] ImageEx tabBgImage;
+    [SerializeField] ButtonEx tabButton;
+    [SerializeField] RectTransform chooseRect;
+    [SerializeField] RectTransform maskRect;
+    [SerializeField] TextEx rateText;
+    HeroSkinFlashSaleManager manager => HeroSkinFlashSaleManager.Instance;
+    public void Display(int index, List<int> list)
+    {
+        if (list?.Count <= index) return;
+
+        var skinID = list[index];
+
+        tabBgImage.SetSprite(StringUtility.Concat("HeroSkinFlashSaleTabInfo_", skinID.ToString()));
+        tabBgImage.SetNativeSize();
+        maskRect.SetActive(manager.currentChooseSkinID != skinID);
+        chooseRect.SetActive(manager.currentChooseSkinID == skinID);
+        tabButton.SetListener(() =>
+        {
+            manager.currentChooseSkinID = skinID;
+        });
+        
+        var act = manager.GetActInfo();
+        if (act == null) return;
+        var config = ActSpecialSaleConfig.Get(act.CfgID);
+        if (config?.PercentageTexts == null || config.PercentageTexts.Length <= index) return;
+        rateText.text = Language.Get("storename5", config.PercentageTexts[index].ToString());
+    }
+}
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs.meta b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs.meta
new file mode 100644
index 0000000..1ccb4bb
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleTabCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1cdbf2b01a9ccb7459cf78b0cd8796fd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs
new file mode 100644
index 0000000..81086ba
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs
@@ -0,0 +1,229 @@
+using System.Collections.Generic;
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class HeroSkinFlashSaleWin : UIBase
+{
+    [SerializeField] float modelScale = 1f;
+    [SerializeField] RawImage bgImage;
+    [SerializeField] ImageEx heroNameImage;
+    [SerializeField] ImageEx skinInfoImage;
+    [SerializeField] ImageEx awardBgImage;
+    [SerializeField] TextEx timeText;
+    [SerializeField] ButtonEx buyButton;
+    [SerializeField] ImageEx buyImage;
+    [SerializeField] ImageEx countryImage;
+    [SerializeField] TextEx buyText;
+    [SerializeField] ImageEx originalPriceImage;
+    [SerializeField] TextEx originalPriceText;
+    [SerializeField] ButtonEx closeButton;
+    [SerializeField] UIHeroController uiHeroController;
+    [SerializeField] UIHeroController lhController;
+    [SerializeField] ScrollerController awardScroller;
+    [SerializeField] ScrollerController tabScroller;
+    [SerializeField] Color numColor;
+    [SerializeField] TextEx[] wearAttrText;
+    [SerializeField] TextEx[] roleAttrText;
+    [SerializeField] ButtonEx infoButton;
+    HeroSkinFlashSaleManager manager => HeroSkinFlashSaleManager.Instance;
+    protected override void InitComponent()
+    {
+        closeButton.SetListener(CloseWindow);
+        infoButton.SetListener(() =>
+        {
+            int heroID = manager.GetHeroIDBySkinID(manager.currentChooseSkinID);
+            HeroUIManager.Instance.selectForPreviewHeroID = heroID;
+            HeroUIManager.Instance.selectSkinIndex = manager.GetSkinIndexInHeroConfig(heroID, manager.currentChooseSkinID);
+            UIManager.Instance.OpenWindow<HeroBestBaseWin>(1);
+        });
+    }
+
+    protected override void OnPreOpen()
+    {
+        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
+        RechargeManager.Instance.rechargeCountEvent += OnRechargeCountEvent;
+        awardScroller.OnRefreshCell += OnRefreshAwardCell;
+        tabScroller.OnRefreshCell += OnRefreshTabCell;
+        manager.OnCurrentChooseSkinIDChangeEevent += OnCurrentChooseSkinIDChange;
+        CreateTabScroller();
+        Display();
+    }
+
+    protected override void OnPreClose()
+    {
+        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
+        RechargeManager.Instance.rechargeCountEvent -= OnRechargeCountEvent;
+        awardScroller.OnRefreshCell -= OnRefreshAwardCell;
+        tabScroller.OnRefreshCell -= OnRefreshTabCell;
+        manager.OnCurrentChooseSkinIDChangeEevent -= OnCurrentChooseSkinIDChange;
+    }
+
+    private void OnCurrentChooseSkinIDChange()
+    {
+        tabScroller.m_Scorller.RefreshActiveCellViews();
+        Display();
+    }
+
+    void OnRefreshTabCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell as HeroSkinFlashSaleTabCell;
+        _cell.Display(cell.index, skinIDList);
+    }
+
+    List<int> skinIDList;
+    void CreateTabScroller()
+    {
+        var act = manager.GetActInfo();
+        if (act == null) return;
+
+        var config = ActSpecialSaleConfig.Get(act.CfgID);
+        if (config == null) return;
+
+        int heroID = manager.GetHeroIDBySkinID(manager.currentChooseSkinID);
+        skinIDList = manager.GetSkinIDList(act.CfgID);
+        if (skinIDList.IsNullOrEmpty()) return;
+        manager.currentChooseSkinID = skinIDList[0];
+
+        tabScroller.Refresh();
+        for (int i = 0; i < skinIDList.Count; i++)
+        {
+            tabScroller.AddCell(ScrollerDataType.Header, i);
+        }
+        tabScroller.Restart();
+    }
+
+    int[][] arr = null;
+    void OnRefreshAwardCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell as HeroSkinFlashSaleAwardCell;
+        _cell.Display(cell.index, arr);
+    }
+
+    void CreateAwardScroller(int[][] sourceArr)
+    {
+        if (sourceArr == null) return;
+
+        // 1. 鑾峰彇宸叉嫢鏈夌殑 ID 闆嗗悎 (浣跨敤 LINQ 绠�娲佹槑浜�)
+        var ownedItems = HeroSkinAttrConfig.GetItemList();
+        var itemIds = ownedItems != null ? new HashSet<int>(ownedItems) : new HashSet<int>();
+
+        // 2. 杩囨护鎺� items 涓凡鏈夌殑椤癸紝骞剁洿鎺ヨ祴鍊肩粰鎴愬憳鍙橀噺
+        // arr[i][0] 鍋囪涓哄垽鏂槸鍚﹀瓨鍦ㄤ簬 items 涓殑 ID
+        this.arr = System.Array.FindAll(sourceArr, row => !itemIds.Contains(row[0]));
+
+        // 3. 鍒锋柊 UI
+        awardScroller.Refresh();
+        for (int i = 0; i < this.arr.Length; i++)
+        {
+            awardScroller.AddCell(ScrollerDataType.Header, i);
+        }
+        awardScroller.Restart();
+    }
+
+    private void OnSecondEvent()
+    {
+        manager.GetActTimeStr(timeText);
+    }
+
+    private void OnRechargeCountEvent(int obj)
+    {
+        Display();
+    }
+
+    public void Display()
+    {
+        var act = manager.GetActInfo();
+        if (act == null) return;
+
+        var config = ActSpecialSaleConfig.Get(act.CfgID);
+        if (config == null) return;
+
+        int heroID = manager.GetHeroIDBySkinID(manager.currentChooseSkinID);
+
+        var heroConfig = HeroConfig.Get(heroID);
+        if (heroConfig == null) return;
+
+        int skinID = manager.currentChooseSkinID;
+
+        var heroSkinAttrConfig = HeroSkinAttrConfig.Get(skinID);
+        if (heroSkinAttrConfig == null) return;
+        if (heroSkinAttrConfig.WearAttrIDList == null) return;
+        if (heroSkinAttrConfig.WearAttrValueList == null) return;
+        if (heroSkinAttrConfig.RoleAttrIDList == null) return;
+        if (heroSkinAttrConfig.RoleAttrValueList == null) return;
+
+        var skinIDList = manager.GetSkinIDList(act.CfgID);
+        if (skinIDList.IsNullOrEmpty()) return;
+
+        int ctgId = manager.GetCtgIDBySkinID(skinID);
+        var ctgConfig = CTGConfig.Get(ctgId);
+        if (ctgConfig == null) return;
+
+        if (!RechargeManager.Instance.TryGetOrderInfo(ctgId, out var orderConfig)) return;
+        if (!RechargeManager.Instance.TryGetRechargeCount(ctgId, out var rechargeCount)) return;
+        if (!RechargeManager.Instance.TryGetRechargeItem(ctgId, out var rechargeItemList)) return;
+
+        bgImage.SetTexture2D(StringUtility.Concat("HeroSkinFlashSaleBG_", skinID.ToString()));
+
+        heroNameImage.SetSprite(StringUtility.Concat("HeroSkinFlashSaleHeroName_", skinID.ToString()));
+        heroNameImage.SetNativeSize();
+
+        awardBgImage.SetSprite(StringUtility.Concat("HeroSkinFlashSaleAwardBG_", skinID.ToString()));
+        awardBgImage.SetNativeSize();
+
+        uiHeroController.Create(skinID, modelScale);
+        lhController.Create(skinID, 1, motionName: "", isLh: true);
+        countryImage.SetSprite(HeroUIManager.Instance.GetCountryIconName(heroConfig.Country));
+        OnSecondEvent();
+
+        CreateAwardScroller(ctgConfig.GainItemList);
+
+        bool isCanBuy = manager.IsNoSellOutCTGID(ctgId);
+        //buyImage.gray = !isCanBuy;
+        buyText.text = !isCanBuy ? Language.Get("storename11") : Language.Get("PayMoneyNum", UIHelper.GetMoneyFormat(orderConfig.PayRMBNumOnSale));
+
+        originalPriceImage.SetActive(isCanBuy);
+        // 鏍规嵁 ctgId 鍦� CTGIDList 涓殑绱㈠紩锛屼粠 OriginalPrices 鍙栧師浠�
+        int originalPriceIndex = System.Array.IndexOf(config.CTGIDList, ctgId);
+        float originalPrice = originalPriceIndex >= 0 && config.OriginalPrices.Length > originalPriceIndex ? config.OriginalPrices[originalPriceIndex] : 0;
+        originalPriceText.text = Language.Get("storename9",Language.Get("PayMoneyNum", UIHelper.GetMoneyFormat(originalPrice)));
+
+
+        buyButton.interactable = isCanBuy;
+        buyButton.SetListener(() =>
+        {
+            RechargeManager.Instance.CTG(ctgId);
+        });
+
+        for (int i = 0; i < wearAttrText.Length; i++)
+            SetAttrInfo(0, i, heroSkinAttrConfig, wearAttrText[i]);
+
+        for (int i = 0; i < roleAttrText.Length; i++)
+            SetAttrInfo(1, i, heroSkinAttrConfig, roleAttrText[i]);
+
+    }
+
+    // type 0 绌挎埓灞炴�у�� 1 涓诲叕灞炴��
+    public void SetAttrInfo(int type, int index, HeroSkinAttrConfig heroSkinAttrConfig, TextEx info)
+    {
+        if (heroSkinAttrConfig == null) return;
+
+        int[] arrID = type == 0 ? heroSkinAttrConfig.WearAttrIDList : heroSkinAttrConfig.RoleAttrIDList;
+        int[] arrValue = type == 0 ? heroSkinAttrConfig.WearAttrValueList : heroSkinAttrConfig.RoleAttrValueList;
+        if (arrID?.Length <= index || arrValue?.Length <= index)
+        {
+            info.text = string.Empty;
+            return;
+        }
+
+        info.text = GetFullDescription(arrID[index], arrValue[index]);
+    }
+
+    string GetFullDescription(int id, long value)
+    {
+        var config = PlayerPropertyConfig.Get(id);
+        if (config == null) return string.Empty;
+        return Language.Get("HeroDebut33", config.ShowName, PlayerPropertyConfig.GetValueDescription(id, value));
+    }
+}
diff --git a/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs.meta b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs.meta
new file mode 100644
index 0000000..601cc09
--- /dev/null
+++ b/Main/System/HeroSkinFlashSale/HeroSkinFlashSaleWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d3368c493a4730a45bd445fbcf6ffa3d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/HeroUI/HeroSkinWin.cs b/Main/System/HeroUI/HeroSkinWin.cs
index a97603c..c27c4db 100644
--- a/Main/System/HeroUI/HeroSkinWin.cs
+++ b/Main/System/HeroUI/HeroSkinWin.cs
@@ -27,6 +27,7 @@
 
     [SerializeField] ScrollerController skinScroller;
     [SerializeField] Button unlockBtn;
+    [SerializeField] ImageEx actBubbleImg;
     [SerializeField] Image itemIcon;
     [SerializeField] Text itemCountText;
     [SerializeField] Button putonBtn;
@@ -326,18 +327,72 @@
                 itemIcon.SetItemSprite(itemID);
                 itemCountText.text = UIHelper.ShowUseItem(PackType.Item, itemID, 1, bright: false);
             }
-            putonBtn.SetActive(false);
-            putonYetObj.SetActive(false);
+        putonBtn.SetActive(false);
+        putonYetObj.SetActive(false);
+        RefreshActBubbleImg();
+        return;
+    }
+    unlockBtn.SetActive(false);
+
+    if (hero == null) return;
+
+    putonBtn.SetActive(hero.SkinAttrID != skinID);
+    putonYetObj.SetActive(hero.SkinAttrID == skinID);
+
+
+    }
+
+    void RefreshActBubbleImg()
+    {
+        if (HeroUIManager.Instance.IsHeroSkinActive(heroID, skinID))
+        {
+            actBubbleImg.SetActive(false);
             return;
         }
-        unlockBtn.SetActive(false);
 
-        if (hero == null) return;
+        if (HeroSkinFlashSaleManager.Instance.IsHeroSkinFlashSaleOpen())
+        {
+            var act = HeroSkinFlashSaleManager.Instance.GetActInfo();
+            if (act != null)
+            {
+                var skinList = HeroSkinFlashSaleManager.Instance.GetSkinIDList(act.CfgID);
+                if (skinList != null && skinList.Contains(skinID))
+                {
+                    actBubbleImg.SetActive(true);
+                    return;
+                }
+            }
+        }
 
-        putonBtn.SetActive(hero.SkinAttrID != skinID);
-        putonYetObj.SetActive(hero.SkinAttrID == skinID);
+        if (HeroDebutManager.Instance.IsHeroDebutOpen())
+        {
+            var act = HeroDebutManager.Instance.GetOperationHeroAppearInfo();
+            if (act != null)
+            {
+                var skinList = HeroDebutManager.Instance.GetSkinIDList(act.CfgID, heroID, 0);
+                if (skinList != null && skinList.Contains(skinID))
+                {
+                    actBubbleImg.SetActive(true);
+                    return;
+                }
+            }
+        }
 
+        if (HeroReturnManager.Instance.IsHeroReturnOpen())
+        {
+            var act = HeroReturnManager.Instance.GetOperationHeroAppearInfo();
+            if (act != null)
+            {
+                var skinList = HeroReturnManager.Instance.GetSkinIDList(act.CfgID, heroID, 0);
+                if (skinList != null && skinList.Contains(skinID))
+                {
+                    actBubbleImg.SetActive(true);
+                    return;
+                }
+            }
+        }
 
+        actBubbleImg.SetActive(false);
     }
 
     void ChangeParentWinAlpha(float alpha)
diff --git a/Main/System/Main/HomeWin.cs b/Main/System/Main/HomeWin.cs
index 5b72e68..aa45add 100644
--- a/Main/System/Main/HomeWin.cs
+++ b/Main/System/Main/HomeWin.cs
@@ -74,6 +74,7 @@
     [SerializeField] TimeRushCell timeRushCell;
     [SerializeField] HeroDebutCell heroDebutCell;
     [SerializeField] HeroReturnCell heroReturnCell;
+    [SerializeField] HeroSkinFlashSaleCell heroSkinFlashSaleCell;
     [SerializeField] Button festivalActivityBtn;
 
     //鍧愰獞
@@ -302,6 +303,7 @@
         DisplayTimeRush();
         DisplayHeroDebut();
         DisplayHeroReturn();
+        DisplayHeroSkinFlash();
         DisplayFestivalActivity();
         DelayPlayMusic().Forget();
 
@@ -355,6 +357,10 @@
         {
             DisplayFestivalActivity();
         }
+        else if (type == OperationType.HeroSkinFlashSale)
+        {
+            DisplayHeroSkinFlash();
+        }
     }
 
     private void OpenServerActivityStateChange()
@@ -364,6 +370,7 @@
         DisplayHeroReturn();
         DisplayGalaBtn();
         DisplayFestivalActivity();
+        DisplayHeroSkinFlash();
     }
 
     private void OnShowGiftIdListAddEvent()
@@ -870,6 +877,10 @@
         {
             DisplayFestivalActivity();
         }
+        else if (funcId == (int)FuncOpenEnum.HeroSkinFlashSale)
+        {
+            DisplayHeroSkinFlash();
+        }
     }
 
     private void OnUpdateFirstChargeInfo()
@@ -973,6 +984,15 @@
         heroReturnCell.Display();
     }
 
+    void DisplayHeroSkinFlash()
+    {
+        bool isOpen = HeroSkinFlashSaleManager.Instance.IsHeroSkinFlashSaleOpen();
+        heroSkinFlashSaleCell.SetActive(isOpen);
+        if (!isOpen)
+            return;
+        heroSkinFlashSaleCell.Display();
+    }
+
     void DisplayFestivalActivity()
     {
         bool isOpen = FestivalActivityManager.Instance.IsFestivalActivityOpen();
diff --git a/Main/System/OpenServerActivity/OperationTimeHepler.cs b/Main/System/OpenServerActivity/OperationTimeHepler.cs
index d1c85c3..fa71e93 100644
--- a/Main/System/OpenServerActivity/OperationTimeHepler.cs
+++ b/Main/System/OpenServerActivity/OperationTimeHepler.cs
@@ -272,6 +272,9 @@
         var opreationType = OperationType.FestivalActivity;
         switch (package.ActNum)
         {
+            case 10:
+                opreationType = OperationType.HeroSkinFlashSale;
+                break;
             case 30:
                 opreationType = OperationType.FestivalActivity;
                 break;
@@ -754,5 +757,6 @@
     FestivalActivity_RechargeTotal = 8,   //鑺傛棩娲诲姩-绱厖鍊�
     FestivalActivity_CheckIn = 9,   //鑺傛棩娲诲姩-绛惧埌
     FestivalActivity_Mission = 10,   //鑺傛棩娲诲姩-浠诲姟
+    HeroSkinFlashSale = 11, //鏃惰鐗瑰崠
     max,
 }
\ No newline at end of file
diff --git a/Main/System/Redpoint/MainRedDot.cs b/Main/System/Redpoint/MainRedDot.cs
index e4325f9..99142df 100644
--- a/Main/System/Redpoint/MainRedDot.cs
+++ b/Main/System/Redpoint/MainRedDot.cs
@@ -157,6 +157,7 @@
     public const int RedPoint_OSMingge = 483;
     public const int HeroReturnRepoint = 484;   //姝﹀皢杩斿満
     public const int FestivalActivityRepoint = 485;    //鑺傛棩娲诲姩
+    public const int HeroSkinFlashSaleRepoint = 486;
     public void Register()
     {
 
diff --git a/Main/System/Store/SkinStoreCell.cs b/Main/System/Store/SkinStoreCell.cs
index fc34772..69c4243 100644
--- a/Main/System/Store/SkinStoreCell.cs
+++ b/Main/System/Store/SkinStoreCell.cs
@@ -19,7 +19,7 @@
 
     public void Display(int index)
     {
-        var list = StoreModel.Instance.storeTypeDict[(int)StoreFunc.HeroSkin];
+        var list = StoreModel.Instance.GetTimeValidStoreDatas(StoreFunc.HeroSkin);
         var storeData = list[index];
         int shopID = storeData.shopId;
         var itemID = storeData.storeConfig.ItemID;
diff --git a/Main/System/Store/SkinStoreLineCell.cs b/Main/System/Store/SkinStoreLineCell.cs
index d637c35..af2f769 100644
--- a/Main/System/Store/SkinStoreLineCell.cs
+++ b/Main/System/Store/SkinStoreLineCell.cs
@@ -10,7 +10,7 @@
 
     public void Display(int index)
     {
-        var list = StoreModel.Instance.storeTypeDict[(int)StoreFunc.HeroSkin];
+        var list = StoreModel.Instance.GetTimeValidStoreDatas(StoreFunc.HeroSkin);
 
         for (int i = 0; i < storeCells.Length; i++)
         {
diff --git a/Main/System/Store/SkinStoreWin.cs b/Main/System/Store/SkinStoreWin.cs
index b9ff542..a660afd 100644
--- a/Main/System/Store/SkinStoreWin.cs
+++ b/Main/System/Store/SkinStoreWin.cs
@@ -25,6 +25,7 @@
         scroller.OnRefreshCell += OnRefreshCell;
         StoreModel.Instance.RefreshShopEvent += Show;
         StoreModel.Instance.RefreshBuyShopLimitEvent += Show;
+        TimeMgr.Instance.OnDayEvent += OnDayEvent;
         
         Display();
     }
@@ -34,6 +35,12 @@
         scroller.OnRefreshCell -= OnRefreshCell;
         StoreModel.Instance.RefreshShopEvent -= Show;
         StoreModel.Instance.RefreshBuyShopLimitEvent -= Show;
+        TimeMgr.Instance.OnDayEvent -= OnDayEvent;
+    }
+
+    void OnDayEvent()
+    {
+        Display();
     }
 
     void Display()
@@ -55,7 +62,7 @@
 
         int jumpIndex = -1;
         scroller.Refresh();
-        var list = StoreModel.Instance.storeTypeDict[(int)StoreFunc.HeroSkin];
+        var list = StoreModel.Instance.GetTimeValidStoreDatas(StoreFunc.HeroSkin);
         for (int i = 0; i < list.Count; i++)
         {
             if (i % 4 == 0)
diff --git a/Main/System/Store/StoreModel.cs b/Main/System/Store/StoreModel.cs
index 32856e4..5d167b4 100644
--- a/Main/System/Store/StoreModel.cs
+++ b/Main/System/Store/StoreModel.cs
@@ -181,7 +181,36 @@
         return datas;
     }
 
+    /// <summary>
+    /// 鑾峰彇鍦ㄤ笂涓嬫灦鏃堕棿鑼冨洿鍐呯殑鍟嗗搧鍒楄〃
+    /// </summary>
+    public List<StoreData> GetTimeValidStoreDatas(StoreFunc type)
+    {
+        List<StoreData> allDatas = TryGetStoreDatas(type);
+        if (allDatas == null)
+            return null;
 
+        int curOpenDay = TimeUtility.OpenDay + 1;
+        List<StoreData> validDatas = new List<StoreData>();
+        for (int i = 0; i < allDatas.Count; i++)
+        {
+            StoreConfig cfg = allDatas[i].storeConfig;
+            // <=0 鎴� StartTime >= EndTime 瑙嗕负鏃犻檺鍒�
+            if (cfg.StartTime <= 0 || cfg.EndTime <= 0 || cfg.StartTime >= cfg.EndTime)
+            {
+                validDatas.Add(allDatas[i]);
+                continue;
+            }
+            // 鏈埌涓婃灦鏃堕棿
+            if (curOpenDay < cfg.StartTime)
+                continue;
+            // 宸茶繃涓嬫灦鏃堕棿
+            if (curOpenDay >= cfg.EndTime)
+                continue;
+            validDatas.Add(allDatas[i]);
+        }
+        return validDatas;
+    }
 
  
     public StoreData GetStoreData(int shopId)
diff --git a/Main/System/UIBase/UIJumpManager.cs b/Main/System/UIBase/UIJumpManager.cs
index 1939e5d..7667b1c 100644
--- a/Main/System/UIBase/UIJumpManager.cs
+++ b/Main/System/UIBase/UIJumpManager.cs
@@ -98,7 +98,7 @@
 		//姝﹀皢杩斿満鐩稿叧
 		else if (config.WinName == "HeroReturnCallWin" || config.WinName == "HeroReturnSkinWin" ||
 				config.WinName == "HeroReturnCheckInWin" || config.WinName == "HeroReturnGiftWin" ||
-				config.WinName == "HeroReturnShopWin"|| config.WinName == "HeroReturnZhanLingWin")
+				config.WinName == "HeroReturnShopWin" || config.WinName == "HeroReturnZhanLingWin")
 		{
 			var heroDebutAct = HeroReturnManager.Instance.GetOperationHeroAppearInfo();
 			if (heroDebutAct == null)
@@ -129,6 +129,28 @@
 				UIManager.Instance.OpenWindow(config.WinName);
 			}
 		}
+		//鐨偆闄愭椂鐗规儬鐩稿叧
+		else if (config.WinName == "HeroSkinFlashSaleWin")
+		{
+			var act = HeroSkinFlashSaleManager.Instance.GetActInfo();
+			if (act == null)
+			{
+				SysNotifyMgr.Instance.ShowTip("ActivityNoOpen");
+				return;
+			}
+			var actConfig = ActSpecialSaleConfig.Get(act.CfgID);
+			if (actConfig == null)
+			{
+				SysNotifyMgr.Instance.ShowTip("ActivityNoOpen");
+				return;
+			}
+
+			if (UIManager.Instance.IsOpened(config.WinName))
+			{
+				UIManager.Instance.CloseWindow(config.WinName);
+				UIManager.Instance.OpenWindow(config.WinName);
+			}
+		}
 
 		if (!UIManager.Instance.IsOpened(config.WinName))
 		{
diff --git a/Main/Utility/EnumHelper.cs b/Main/Utility/EnumHelper.cs
index 845f48a..31ec754 100644
--- a/Main/Utility/EnumHelper.cs
+++ b/Main/Utility/EnumHelper.cs
@@ -860,6 +860,7 @@
     HeroDebut = 63,//姝﹀皢鐧诲満
     HeroReturn = 64,  //姝﹀皢杩斿満
     FestivalActivity = 65,//鑺傛棩娲诲姩
+    HeroSkinFlashSale = 66,//鏃惰鐗瑰崠
 }
 
 

--
Gitblit v1.8.0