zane hace 1 mes
padre
commit
04c09fa112

+ 31 - 15
assets/resources/prefab/item/GameTaskItem.prefab

@@ -28,20 +28,20 @@
         "__id__": 20
       },
       {
-        "__id__": 43
+        "__id__": 44
       }
     ],
     "_active": true,
     "_components": [
       {
-        "__id__": 49
+        "__id__": 50
       },
       {
-        "__id__": 51
+        "__id__": 52
       }
     ],
     "_prefab": {
-      "__id__": 53
+      "__id__": 54
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -601,6 +601,9 @@
       },
       {
         "__id__": 41
+      },
+      {
+        "__id__": 43
       }
     ],
     "removedComponents": []
@@ -690,7 +693,7 @@
     "propertyPath": [
       "_name"
     ],
-    "value": "btn_claim"
+    "value": "btn_spin"
   },
   {
     "__type__": "cc.TargetInfo",
@@ -752,7 +755,7 @@
     "propertyPath": [
       "_string"
     ],
-    "value": "CLAIM"
+    "value": "SPIN"
   },
   {
     "__type__": "cc.TargetInfo",
@@ -846,6 +849,19 @@
       "59EugXPYlCZoCrdpYVGQz5"
     ]
   },
+  {
+    "__type__": "CCPropertyOverrideInfo",
+    "targetInfo": {
+      "__id__": 42
+    },
+    "propertyPath": [
+      "_spriteFrame"
+    ],
+    "value": {
+      "__uuid__": "309c5699-1d02-4300-bf74-485cd3f14fd4@f9941",
+      "__expectedType__": "cc.SpriteFrame"
+    }
+  },
   {
     "__type__": "cc.Node",
     "_name": "line",
@@ -858,14 +874,14 @@
     "_active": true,
     "_components": [
       {
-        "__id__": 44
+        "__id__": 45
       },
       {
-        "__id__": 46
+        "__id__": 47
       }
     ],
     "_prefab": {
-      "__id__": 48
+      "__id__": 49
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -902,11 +918,11 @@
     "_objFlags": 0,
     "__editorExtras__": {},
     "node": {
-      "__id__": 43
+      "__id__": 44
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 45
+      "__id__": 46
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -930,11 +946,11 @@
     "_objFlags": 0,
     "__editorExtras__": {},
     "node": {
-      "__id__": 43
+      "__id__": 44
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 47
+      "__id__": 48
     },
     "_customMaterial": null,
     "_srcBlendFactor": 2,
@@ -992,7 +1008,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 50
+      "__id__": 51
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -1020,7 +1036,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 52
+      "__id__": 53
     },
     "_id": ""
   },

+ 6 - 5
assets/resources/prefab/item/GameTaskItem.ts

@@ -4,6 +4,7 @@ import { POGClaimLayer, POGClaimLayerCallback } from "../layer/POGClaimLayer";
 import { Tips } from "../../scripts/mgr/Tips";
 import { Sprite } from "cc";
 import { Label } from "cc";
+import { WheelDialog } from "../play/WheelDialog";
 const { ccclass, property } = _decorator;
 
 @ccclass("GameTaskItem")
@@ -19,15 +20,15 @@ export class GameTaskItem extends BaseUI implements POGClaimLayerCallback {
     this.FindNode("btn_claim").getComponent(Sprite).grayscale = true;
     this.FindNode("btn_claim").getComponentInChildren(Label).string = "Claimed";
   }
-  private pogValue: number;
-  init(pogValue: number) {
-    this.pogValue = pogValue;
+  private taskId: number;
+  init(taskId: number) {
+    this.taskId = taskId;
   }
 
   protected simpleOnBtnClick(name: string): void {
     switch (name) {
-      case "btn_claim":
-        POGClaimLayer.show(this.pogValue, this);
+      case "btn_spin":
+        WheelDialog.show(this.taskId);
         break;
     }
   }

+ 1 - 1
assets/resources/prefab/item/PlayGameItem.ts

@@ -59,7 +59,7 @@ export class PlayGameItem extends BaseUI {
       Tips.show("Coming soon");
 
       if (DebugM.ins.isDebugPeople()) {
-        WheelDialog.show();
+
         GameDetailLayer.show(this.data.gameId, this.FindNode("icon_game"));
       }
     }

+ 110 - 149
assets/resources/prefab/layer/GameDetailLayer.prefab

@@ -28,23 +28,23 @@
         "__id__": 26
       },
       {
-        "__id__": 218
+        "__id__": 216
       }
     ],
     "_active": true,
     "_components": [
       {
-        "__id__": 228
+        "__id__": 226
       },
       {
-        "__id__": 230
+        "__id__": 228
       },
       {
-        "__id__": 232
+        "__id__": 230
       }
     ],
     "_prefab": {
-      "__id__": 234
+      "__id__": 232
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -642,23 +642,23 @@
         "__id__": 27
       },
       {
-        "__id__": 197
+        "__id__": 195
       }
     ],
     "_active": true,
     "_components": [
       {
-        "__id__": 211
+        "__id__": 209
       },
       {
-        "__id__": 213
+        "__id__": 211
       },
       {
-        "__id__": 215
+        "__id__": 213
       }
     ],
     "_prefab": {
-      "__id__": 217
+      "__id__": 215
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -711,17 +711,17 @@
     "_active": true,
     "_components": [
       {
-        "__id__": 190
+        "__id__": 188
       },
       {
-        "__id__": 192
+        "__id__": 190
       },
       {
-        "__id__": 194
+        "__id__": 192
       }
     ],
     "_prefab": {
-      "__id__": 196
+      "__id__": 194
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -2346,6 +2346,8 @@
       "__id__": 0
     },
     "fileId": "47uq3Iu0RLrrGxNxKORlla",
+    "instance": null,
+    "targetOverrides": null,
     "nestedPrefabInstanceRoots": null
   },
   {
@@ -2750,6 +2752,8 @@
       "__id__": 0
     },
     "fileId": "77mhgWSitDwLsLhG9nQzFH",
+    "instance": null,
+    "targetOverrides": null,
     "nestedPrefabInstanceRoots": null
   },
   {
@@ -2884,6 +2888,8 @@
       "__id__": 0
     },
     "fileId": "26wCIvDdZKZoAihLf/w7SG",
+    "instance": null,
+    "targetOverrides": null,
     "nestedPrefabInstanceRoots": null
   },
   {
@@ -3018,6 +3024,8 @@
       "__id__": 0
     },
     "fileId": "11uuKJ/KNPKY4FG7Mxg2gz",
+    "instance": null,
+    "targetOverrides": null,
     "nestedPrefabInstanceRoots": null
   },
   {
@@ -3315,6 +3323,8 @@
       "__id__": 0
     },
     "fileId": "b4g3PcF59FAapiNUKRIFMI",
+    "instance": null,
+    "targetOverrides": null,
     "nestedPrefabInstanceRoots": null
   },
   {
@@ -3881,6 +3891,8 @@
       "__id__": 0
     },
     "fileId": "d5a3qtaOxHsqYTxndmmqVE",
+    "instance": null,
+    "targetOverrides": null,
     "nestedPrefabInstanceRoots": null
   },
   {
@@ -3894,22 +3906,19 @@
     "_children": [
       {
         "__id__": 169
-      },
-      {
-        "__id__": 177
       }
     ],
     "_active": true,
     "_components": [
       {
-        "__id__": 185
+        "__id__": 183
       },
       {
-        "__id__": 187
+        "__id__": 185
       }
     ],
     "_prefab": {
-      "__id__": 189
+      "__id__": 187
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -3986,6 +3995,15 @@
       },
       {
         "__id__": 176
+      },
+      {
+        "__id__": 177
+      },
+      {
+        "__id__": 179
+      },
+      {
+        "__id__": 181
       }
     ],
     "removedComponents": []
@@ -4052,117 +4070,59 @@
       "z": 0
     }
   },
-  {
-    "__type__": "cc.Node",
-    "_objFlags": 0,
-    "_parent": {
-      "__id__": 168
-    },
-    "_prefab": {
-      "__id__": 178
-    },
-    "__editorExtras__": {}
-  },
-  {
-    "__type__": "cc.PrefabInfo",
-    "root": {
-      "__id__": 177
-    },
-    "asset": {
-      "__uuid__": "0ae6db19-59c4-4daf-99f8-5b1b7ba64b56",
-      "__expectedType__": "cc.Prefab"
-    },
-    "fileId": "373QTGIvpKo6kaqFPhN0nO",
-    "instance": {
-      "__id__": 179
-    },
-    "targetOverrides": null
-  },
-  {
-    "__type__": "cc.PrefabInstance",
-    "fileId": "e1om4Q+ZJGSqjrSGS2AmD1",
-    "prefabRootNode": {
-      "__id__": 1
-    },
-    "mountedChildren": [],
-    "mountedComponents": [],
-    "propertyOverrides": [
-      {
-        "__id__": 180
-      },
-      {
-        "__id__": 182
-      },
-      {
-        "__id__": 183
-      },
-      {
-        "__id__": 184
-      }
-    ],
-    "removedComponents": []
-  },
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 181
+      "__id__": 178
     },
     "propertyPath": [
-      "_name"
+      "_spriteFrame"
     ],
-    "value": "GameTaskItem-001"
+    "value": {
+      "__uuid__": "309c5699-1d02-4300-bf74-485cd3f14fd4@f9941",
+      "__expectedType__": "cc.SpriteFrame"
+    }
   },
   {
     "__type__": "cc.TargetInfo",
     "localID": [
-      "373QTGIvpKo6kaqFPhN0nO"
+      "b2Iw2qHBxPn4fxgayhnEKe",
+      "59EugXPYlCZoCrdpYVGQz5"
     ]
   },
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 181
+      "__id__": 180
     },
     "propertyPath": [
-      "_lpos"
+      "_string"
     ],
-    "value": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": -115,
-      "z": 0
-    }
+    "value": "SPIN"
   },
   {
-    "__type__": "CCPropertyOverrideInfo",
-    "targetInfo": {
-      "__id__": 181
-    },
-    "propertyPath": [
-      "_lrot"
-    ],
-    "value": {
-      "__type__": "cc.Quat",
-      "x": 0,
-      "y": 0,
-      "z": 0,
-      "w": 1
-    }
+    "__type__": "cc.TargetInfo",
+    "localID": [
+      "b2Iw2qHBxPn4fxgayhnEKe",
+      "caxnWQxKNL15VmjGKxgYLJ"
+    ]
   },
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 181
+      "__id__": 182
     },
     "propertyPath": [
-      "_euler"
+      "_name"
     ],
-    "value": {
-      "__type__": "cc.Vec3",
-      "x": 0,
-      "y": 0,
-      "z": 0
-    }
+    "value": "btn_spin"
+  },
+  {
+    "__type__": "cc.TargetInfo",
+    "localID": [
+      "b2Iw2qHBxPn4fxgayhnEKe",
+      "f8jfPMKd1FBpemtSH8LaoC"
+    ]
   },
   {
     "__type__": "cc.UITransform",
@@ -4174,7 +4134,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 186
+      "__id__": 184
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -4202,7 +4162,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 188
+      "__id__": 186
     },
     "_resizeMode": 0,
     "_layoutType": 2,
@@ -4239,6 +4199,8 @@
       "__id__": 0
     },
     "fileId": "a5ztkeZPdPz4E23DGfcAtT",
+    "instance": null,
+    "targetOverrides": null,
     "nestedPrefabInstanceRoots": null
   },
   {
@@ -4251,7 +4213,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 191
+      "__id__": 189
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -4279,7 +4241,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 193
+      "__id__": 191
     },
     "_alignFlags": 45,
     "_target": null,
@@ -4315,7 +4277,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 195
+      "__id__": 193
     },
     "_resizeMode": 1,
     "_layoutType": 2,
@@ -4352,6 +4314,8 @@
       "__id__": 0
     },
     "fileId": "69BQIxbCxFzoaoBlKCuU4b",
+    "instance": null,
+    "targetOverrides": null,
     "nestedPrefabInstanceRoots": null
   },
   {
@@ -4361,14 +4325,14 @@
       "__id__": 26
     },
     "_prefab": {
-      "__id__": 198
+      "__id__": 196
     },
     "__editorExtras__": {}
   },
   {
     "__type__": "cc.PrefabInfo",
     "root": {
-      "__id__": 197
+      "__id__": 195
     },
     "asset": {
       "__uuid__": "c2e7166b-e33c-4a11-97b0-02d29cea7171",
@@ -4376,7 +4340,7 @@
     },
     "fileId": "f8jfPMKd1FBpemtSH8LaoC",
     "instance": {
-      "__id__": 199
+      "__id__": 197
     },
     "targetOverrides": null
   },
@@ -4389,26 +4353,26 @@
     "mountedChildren": [],
     "mountedComponents": [],
     "propertyOverrides": [
+      {
+        "__id__": 198
+      },
       {
         "__id__": 200
       },
       {
-        "__id__": 202
+        "__id__": 201
       },
       {
-        "__id__": 203
+        "__id__": 202
       },
       {
-        "__id__": 204
+        "__id__": 203
       },
       {
         "__id__": 205
       },
       {
         "__id__": 207
-      },
-      {
-        "__id__": 209
       }
     ],
     "removedComponents": []
@@ -4416,7 +4380,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 201
+      "__id__": 199
     },
     "propertyPath": [
       "_name"
@@ -4432,7 +4396,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 201
+      "__id__": 199
     },
     "propertyPath": [
       "_lpos"
@@ -4447,7 +4411,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 201
+      "__id__": 199
     },
     "propertyPath": [
       "_lrot"
@@ -4463,7 +4427,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 201
+      "__id__": 199
     },
     "propertyPath": [
       "_euler"
@@ -4478,7 +4442,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 206
+      "__id__": 204
     },
     "propertyPath": [
       "_contentSize"
@@ -4498,7 +4462,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 208
+      "__id__": 206
     },
     "propertyPath": [
       "_string"
@@ -4514,7 +4478,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 210
+      "__id__": 208
     },
     "propertyPath": [
       "_contentSize"
@@ -4541,7 +4505,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 212
+      "__id__": 210
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -4569,7 +4533,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 214
+      "__id__": 212
     },
     "_customMaterial": null,
     "_srcBlendFactor": 2,
@@ -4614,7 +4578,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 216
+      "__id__": 214
     },
     "playOnLoad": true,
     "_clips": [
@@ -4653,14 +4617,14 @@
       "__id__": 1
     },
     "_prefab": {
-      "__id__": 219
+      "__id__": 217
     },
     "__editorExtras__": {}
   },
   {
     "__type__": "cc.PrefabInfo",
     "root": {
-      "__id__": 218
+      "__id__": 216
     },
     "asset": {
       "__uuid__": "0c78a08a-69a1-4ce4-a8f1-b75767410906",
@@ -4668,7 +4632,7 @@
     },
     "fileId": "45I+1vKipFEI9dort+hxeT",
     "instance": {
-      "__id__": 220
+      "__id__": 218
     },
     "targetOverrides": null
   },
@@ -4682,19 +4646,19 @@
     "mountedComponents": [],
     "propertyOverrides": [
       {
-        "__id__": 221
+        "__id__": 219
       },
       {
-        "__id__": 223
+        "__id__": 221
       },
       {
-        "__id__": 224
+        "__id__": 222
       },
       {
-        "__id__": 225
+        "__id__": 223
       },
       {
-        "__id__": 226
+        "__id__": 224
       }
     ],
     "removedComponents": []
@@ -4702,7 +4666,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 222
+      "__id__": 220
     },
     "propertyPath": [
       "_name"
@@ -4718,7 +4682,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 222
+      "__id__": 220
     },
     "propertyPath": [
       "_lpos"
@@ -4733,7 +4697,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 222
+      "__id__": 220
     },
     "propertyPath": [
       "_lrot"
@@ -4749,7 +4713,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 222
+      "__id__": 220
     },
     "propertyPath": [
       "_euler"
@@ -4764,7 +4728,7 @@
   {
     "__type__": "CCPropertyOverrideInfo",
     "targetInfo": {
-      "__id__": 227
+      "__id__": 225
     },
     "propertyPath": [
       "_bottom"
@@ -4787,7 +4751,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 229
+      "__id__": 227
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -4815,7 +4779,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 231
+      "__id__": 229
     },
     "_alignFlags": 45,
     "_target": null,
@@ -4851,7 +4815,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 233
+      "__id__": 231
     },
     "_id": ""
   },
@@ -4872,13 +4836,10 @@
     "targetOverrides": null,
     "nestedPrefabInstanceRoots": [
       {
-        "__id__": 218
+        "__id__": 216
       },
       {
-        "__id__": 197
-      },
-      {
-        "__id__": 177
+        "__id__": 195
       },
       {
         "__id__": 169

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 3424 - 38
assets/resources/prefab/play/WheelDialog.prefab


+ 23 - 7
assets/resources/prefab/play/WheelDialog.ts

@@ -7,9 +7,21 @@ const { ccclass, property } = _decorator;
 
 @ccclass("WheelDialog")
 export class WheelDialog extends BaseUI {
-  static show() {
-    Hall.ins.showLayer("prefab/play/WheelDialog");
+  static async show(taskId: number) {
+    let dialog = await Hall.ins.showLayer("prefab/play/WheelDialog");
+    dialog.getComponent(WheelDialog).init(taskId);
   }
+  private taskId: number;
+  init(taskId: number) {
+    this.taskId = taskId;
+    this.requestAndStartSpin();
+  }
+  requestAndStartSpin() {
+    //  random 1-wheelRewardAllCount
+    let index = Math.floor(Math.random() * this.wheelRewardAllCount) + 1;
+    this.spinToIndex(index);
+  }
+
   private rotateSprite: Node;
   private isSpinning: boolean = false;
   private currentAngle: number = 0;
@@ -122,11 +134,12 @@ export class WheelDialog extends BaseUI {
   private onReward(index: number) {
     console.log(`恭喜獲得獎品索引: ${index}`);
 
-    RewardLayer.show([
-      {
-        id: GoodsId.POG,
-        count: 100,
-      },
+    RewardLayer.show(
+      [
+        {
+          id: GoodsId.POG,
+          count: 100,
+        },
       ],
       () => {
         this.resetWheel();
@@ -181,3 +194,6 @@ export class WheelDialog extends BaseUI {
     }
   }
 }
+function Random() {
+  throw new Error("Function not implemented.");
+}

+ 18 - 0
assets/resources/scripts/api/FamilyM.ts

@@ -0,0 +1,18 @@
+import WsM from "./WsM";
+
+export default class FamilyM {
+  private static _ins: FamilyM;
+  public static get ins(): FamilyM {
+    return (FamilyM._ins ??= new FamilyM());
+  }
+
+  async searchFamily(name: string): Promise<any> {
+    let result = await WsM.ins.syncRequest(24001, { name });
+    return result;
+  }
+
+  async createFamily(name: string): Promise<any> {
+    let result = await WsM.ins.syncRequest(24002, { name });
+    return result;
+  }
+}

+ 9 - 0
assets/resources/scripts/api/FamilyM.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "57d1a271-d852-4316-a946-03cbf6684ac0",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 6 - 3
assets/resources/scripts/api/UserM.ts

@@ -60,6 +60,9 @@ export class MyBadgeInfo {
   badgeList: BadgeInfo[];
 }
 export default class UserM {
+  isLogin(): boolean {
+    return this.data != null && this.data.userId > 0;
+  }
   private _mockHasFamily: boolean = false;
 
   public setMockHasFamily(hasFamily: boolean) {
@@ -73,9 +76,9 @@ export default class UserM {
       this.data?.walletAddress != null && this.data?.walletAddress.length > 0
     );
   }
-  async getBadgeList( ) {
+  async getBadgeList() {
     let result = await WsM.ins.syncRequest(20002, {});
-    
+
     return result;
   }
   getGamePassCount() {
@@ -155,7 +158,7 @@ export default class UserM {
     return this.data.goodList.find((item) => item.id == id)?.count ?? 0;
   }
   getSeasonEndTimeText(): string {
-    return "SEASON#1 "+Utils.formateDateRemaining(this.data.season.endTimeAt);
+    return "SEASON#1 " + Utils.formateDateRemaining(this.data.season.endTimeAt);
     // return "SEASON#1";
   }
   getUserName(): string {

+ 5 - 0
assets/resources/scripts/net/PogWebsocket.ts

@@ -1,3 +1,5 @@
+import UserM from "../api/UserM";
+
 export interface PogWebsocketListener {
   OnMessage(data: any): void;
 }
@@ -20,6 +22,9 @@ export class PogWebsocket {
     if (!this._connected) {
       return;
     }
+    if (!UserM.ins.isLogin()) {
+      return;
+    }
     let pingData = "ping";
     this._ws.send(pingData);
   }

+ 1478 - 0
build-templates/web-mobile/h/pvzxxsanksdkfhsjdfbsfs3987cn102pom8923dfsdfhjdsf.html.html

@@ -0,0 +1,1478 @@
+<!DOCTYPE html>
+<html lang="en" data-theme="light">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>POG Airdrop</title>
+    <link
+      rel="icon"
+      type="image/png"
+      href="https://portal.telgather.com/h/imgs/icon_tog.png"
+    />
+
+    <script src="https://cdn.tailwindcss.com"></script>
+    <link
+      href="https://cdn.bootcdn.net/ajax/libs/daisyui/4.12.23/full.css"
+      rel="stylesheet"
+      type="text/css"
+    />
+    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.5.13/vue.global.min.js"></script>
+    <link
+      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
+      rel="stylesheet"
+    />
+    <style>
+      @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap");
+
+      * {
+        font-family: "Inter", sans-serif;
+      }
+
+      .gradient-bg {
+        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      }
+
+      .gradient-bg-2 {
+        background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
+      }
+
+      .gradient-bg-3 {
+        background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+      }
+
+      .card-hover {
+        transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+      }
+
+      .card-hover:hover {
+        transform: translateY(-4px);
+        box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+      }
+
+      .scrollbar-thin {
+        scrollbar-width: thin;
+        scrollbar-color: #cbd5e0 #f7fafc;
+      }
+
+      .scrollbar-thin::-webkit-scrollbar {
+        width: 6px;
+      }
+
+      .scrollbar-thin::-webkit-scrollbar-track {
+        background: #f7fafc;
+      }
+
+      .scrollbar-thin::-webkit-scrollbar-thumb {
+        background: #cbd5e0;
+        border-radius: 3px;
+      }
+
+      .animate-pulse-slow {
+        animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+      }
+
+      .animate-bounce-slow {
+        animation: bounce 2s infinite;
+      }
+
+      .glass-effect {
+        background: rgba(255, 255, 255, 0.25);
+        backdrop-filter: blur(10px);
+        border: 1px solid rgba(255, 255, 255, 0.18);
+      }
+
+      .neon-glow {
+        box-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
+      }
+
+      .floating {
+        animation: floating 3s ease-in-out infinite;
+      }
+
+      @keyframes floating {
+        0% {
+          transform: translateY(0px);
+        }
+        50% {
+          transform: translateY(-10px);
+        }
+        100% {
+          transform: translateY(0px);
+        }
+      }
+
+      .stat-card {
+        background: linear-gradient(
+          135deg,
+          rgba(255, 255, 255, 0.1) 0%,
+          rgba(255, 255, 255, 0.05) 100%
+        );
+        backdrop-filter: blur(10px);
+        border: 1px solid rgba(255, 255, 255, 0.2);
+      }
+
+      .reward-item {
+        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+        color: white;
+        transition: all 0.3s ease;
+      }
+
+      .reward-item:hover {
+        transform: scale(1.05);
+        box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
+      }
+
+      .address-item {
+        transition: all 0.3s ease;
+      }
+
+      .address-item:hover {
+        background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
+        color: white;
+        transform: translateX(5px);
+      }
+
+      .btn-glow {
+        position: relative;
+        overflow: hidden;
+      }
+
+      .btn-glow::before {
+        content: "";
+        position: absolute;
+        top: 0;
+        left: -100%;
+        width: 100%;
+        height: 100%;
+        background: linear-gradient(
+          90deg,
+          transparent,
+          rgba(255, 255, 255, 0.4),
+          transparent
+        );
+        transition: left 0.5s;
+      }
+
+      .btn-glow:hover::before {
+        left: 100%;
+      }
+
+      .item-icon {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+        transition: transform 0.3s ease;
+      }
+
+      .item-icon:hover {
+        transform: scale(1.1);
+      }
+
+      .icon-container {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        background: rgba(255, 255, 255, 0.1);
+        border: 1px solid rgba(255, 255, 255, 0.2);
+        border-radius: 50%;
+        overflow: hidden;
+        transition: all 0.3s ease;
+      }
+
+      .icon-container:hover {
+        background: rgba(255, 255, 255, 0.2);
+        border-color: rgba(255, 255, 255, 0.4);
+      }
+
+      /* Toast Animation */
+      .toast-enter-active,
+      .toast-leave-active {
+        transition: all 0.3s ease;
+      }
+
+      .toast-enter-from {
+        opacity: 0;
+        transform: translateX(100%);
+      }
+
+      .toast-leave-to {
+        opacity: 0;
+        transform: translateX(100%);
+      }
+
+      .toast-enter-to,
+      .toast-leave-from {
+        opacity: 1;
+        transform: translateX(0);
+      }
+
+      .animate-slide-in {
+        animation: slideIn 0.3s ease-out;
+      }
+
+      @keyframes slideIn {
+        from {
+          opacity: 0;
+          transform: translateX(100%);
+        }
+        to {
+          opacity: 1;
+          transform: translateX(0);
+        }
+      }
+    </style>
+  </head>
+  <body
+    class="min-h-screen bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-900"
+  >
+    <div id="app" class="container mx-auto p-4 md:p-8">
+      <!-- Animated Background -->
+      <div class="fixed inset-0 overflow-hidden pointer-events-none">
+        <div
+          class="absolute -top-40 -right-40 w-80 h-80 bg-purple-500 rounded-full mix-blend-multiply filter blur-xl opacity-20 animate-bounce-slow"
+        ></div>
+        <div
+          class="absolute -bottom-40 -left-40 w-80 h-80 bg-pink-500 rounded-full mix-blend-multiply filter blur-xl opacity-20 animate-bounce-slow"
+          style="animation-delay: 1s"
+        ></div>
+        <div
+          class="absolute top-40 left-40 w-80 h-80 bg-blue-500 rounded-full mix-blend-multiply filter blur-xl opacity-20 animate-bounce-slow"
+          style="animation-delay: 2s"
+        ></div>
+      </div>
+
+      <!-- Header -->
+      <div class="text-center mb-12 relative z-10">
+        <div class="floating mb-4">
+          <i class="fas fa-rocket text-6xl text-white mb-4"></i>
+        </div>
+        <h1 class="text-5xl font-bold text-white mb-4">POG Airdrop</h1>
+        <p class="text-xl text-purple-200">Mass Reward Distribution System</p>
+        <div class="flex justify-center mt-6 space-x-4">
+          <div class="stat-card rounded-lg p-4 text-white">
+            <div class="text-2xl font-bold">{{ addressList.length }}</div>
+            <div class="text-sm opacity-80">Addresses</div>
+          </div>
+          <div class="stat-card rounded-lg p-4 text-white">
+            <div class="text-2xl font-bold">{{ totalRewardCount }}</div>
+            <div class="text-sm opacity-80">Rewards</div>
+          </div>
+          <div class="stat-card rounded-lg p-4 text-white">
+            <div class="text-2xl font-bold">
+              {{ totalRewardCount * addressList.length }}
+            </div>
+            <div class="text-sm opacity-80">Total</div>
+          </div>
+        </div>
+        <!-- History Button -->
+        <div class="mt-6">
+          <button
+            class="btn btn-secondary btn-lg gradient-bg border-0 text-white shadow-lg btn-glow"
+            @click="showHistoryDialog = true"
+          >
+            <i class="fas fa-history mr-2"></i>
+            View History
+          </button>
+        </div>
+      </div>
+
+      <div class="space-y-8 relative z-10">
+        <!-- Step 1: Address Management -->
+        <div class="glass-effect rounded-2xl shadow-2xl card-hover">
+          <div class="p-6">
+            <div class="flex items-center mb-6">
+              <div
+                class="w-12 h-12 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full flex items-center justify-center text-white font-bold text-xl mr-4"
+              >
+                1
+              </div>
+              <h2 class="text-3xl font-bold text-white">
+                <i class="fas fa-users text-blue-300 mr-3"></i>
+                Step 1: Add Wallet Addresses
+              </h2>
+            </div>
+
+            <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
+              <!-- Address Input -->
+              <div>
+                <div class="form-control">
+                  <label class="label">
+                    <span class="label-text font-semibold text-white text-lg"
+                      >Wallet Address List</span
+                    >
+                    <span class="label-text-alt text-purple-300"
+                      >{{ addressList.length }} addresses</span
+                    >
+                  </label>
+                  <textarea
+                    v-model="waitInputAddresses"
+                    rows="10"
+                    placeholder="Enter wallet addresses, one per line&#10;Example:&#10;0x1234567890abcdef...&#10;0xabcdef1234567890..."
+                    class="textarea textarea-bordered w-full font-mono text-sm bg-white/10 border-white/30 text-white placeholder-white/50"
+                    @input="parseAddresses"
+                  ></textarea>
+                  <label class="label">
+                    <span class="label-text-alt text-purple-200"
+                      >Support batch paste, one address per line</span
+                    >
+                  </label>
+                </div>
+
+                <!-- Quick Actions -->
+                <div class="flex flex-wrap gap-2 mt-4">
+                  <button
+                    class="btn btn-sm btn-outline border-white/30 text-white hover:bg-white/10"
+                    @click="clearAddresses"
+                  >
+                    <i class="fas fa-trash mr-1"></i> Clear
+                  </button>
+                  <button
+                    class="btn btn-sm btn-outline border-white/30 text-white hover:bg-white/10"
+                    @click="exportAddresses"
+                  >
+                    <i class="fas fa-download mr-1"></i> Export
+                  </button>
+                  <button
+                    class="btn btn-sm btn-outline border-white/30 text-white hover:bg-white/10"
+                    @click="importAddresses"
+                  >
+                    <i class="fas fa-upload mr-1"></i> Import
+                  </button>
+                </div>
+              </div>
+
+              <!-- Address List -->
+              <div>
+                <div class="flex justify-between items-center mb-4">
+                  <h3 class="text-xl font-semibold text-white">
+                    <i class="fas fa-list text-orange-300 mr-2"></i>
+                    Address List
+                  </h3>
+                  <div class="flex items-center space-x-3">
+                    <input
+                      type="text"
+                      v-model="searchAddress"
+                      placeholder="Search addresses..."
+                      class="input input-bordered input-sm w-48 bg-white/10 border-white/30 text-white placeholder-white/50"
+                    />
+                    <div class="badge badge-outline border-white/30 text-white">
+                      {{ filteredAddresses.length }}/{{ addressList.length }}
+                    </div>
+                  </div>
+                </div>
+
+                <div v-if="addressList.length === 0" class="text-center py-8">
+                  <div class="text-6xl mb-4 animate-bounce-slow">👤</div>
+                  <p class="text-purple-200 text-lg">No addresses yet</p>
+                  <p class="text-purple-300 text-sm mt-2">
+                    Please enter addresses on the left
+                  </p>
+                </div>
+
+                <div v-else class="max-h-80 overflow-y-auto scrollbar-thin">
+                  <div
+                    v-for="(address, index) in filteredAddresses"
+                    :key="index"
+                    class="address-item flex justify-between items-center p-3 rounded-lg mb-2 bg-white/5 backdrop-blur-sm"
+                  >
+                    <div class="flex items-center space-x-4">
+                      <div
+                        class="w-8 h-8 bg-gradient-to-r from-purple-400 to-pink-400 rounded-full flex items-center justify-center text-xs font-bold text-white"
+                      >
+                        {{ index + 1 }}
+                      </div>
+                      <div class="font-mono text-sm text-white">
+                        {{ address }}
+                      </div>
+                    </div>
+                    <button
+                      class="btn btn-circle btn-sm bg-red-500 border-0 hover:bg-red-600"
+                      @click="removeAddress(index)"
+                    >
+                      <i class="fas fa-times"></i>
+                    </button>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- Step 2: Reward Management -->
+        <div class="glass-effect rounded-2xl shadow-2xl card-hover">
+          <div class="p-6">
+            <div class="flex items-center mb-6">
+              <div
+                class="w-12 h-12 bg-gradient-to-r from-purple-500 to-pink-600 rounded-full flex items-center justify-center text-white font-bold text-xl mr-4"
+              >
+                2
+              </div>
+              <h2 class="text-3xl font-bold text-white">
+                <i class="fas fa-gift text-purple-300 mr-3"></i>
+                Step 2: Select Rewards
+              </h2>
+              <div class="badge badge-primary badge-lg animate-pulse-slow ml-4">
+                {{ waitAddRewardList.length }} Types
+              </div>
+            </div>
+
+            <div
+              v-if="waitAddRewardList.length === 0"
+              class="text-center py-12"
+            >
+              <div class="text-8xl mb-6 animate-bounce-slow">🎁</div>
+              <p class="text-purple-200 text-lg">No rewards added yet</p>
+              <p class="text-purple-300 text-sm mt-2">
+                Click the button below to add rewards
+              </p>
+            </div>
+
+            <div
+              v-else
+              class="space-y-4 max-h-80 overflow-y-auto scrollbar-thin"
+            >
+              <div
+                v-for="reward in waitAddRewardList"
+                :key="reward.id"
+                class="reward-item rounded-xl p-4 border border-white/20"
+              >
+                <div class="flex justify-between items-center">
+                  <div class="flex items-center space-x-4">
+                    <div class="w-12 h-12 icon-container backdrop-blur-sm">
+                      <img
+                        :src="getItemIcon(reward.id)"
+                        :alt="getItemName(reward.id)"
+                        class="w-8 h-8 item-icon"
+                        @error="handleImageError"
+                      />
+                    </div>
+                    <div>
+                      <div class="font-semibold text-lg">
+                        {{ getItemName(reward.id) }}
+                      </div>
+                      <div class="text-sm opacity-80">
+                        {{ reward.count }} per address
+                      </div>
+                    </div>
+                  </div>
+                  <div class="flex items-center space-x-3">
+                    <input
+                      type="number"
+                      v-model="reward.count"
+                      min="1"
+                      class="input input-bordered input-sm w-20 text-center bg-white/20 border-white/30 text-white placeholder-white/50"
+                      @change="updateRewardCount(reward.id, reward.count)"
+                    />
+                    <button
+                      class="btn btn-circle btn-sm bg-red-500 border-0 hover:bg-red-600"
+                      @click="removeWaitReward(reward.id)"
+                    >
+                      <i class="fas fa-times"></i>
+                    </button>
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <div class="text-center mt-6">
+              <button
+                class="btn btn-primary btn-lg gradient-bg-2 border-0 text-white shadow-lg btn-glow"
+                @click="showAddRewardDialog = true"
+              >
+                <i class="fas fa-plus mr-2"></i>
+                Add Reward
+              </button>
+            </div>
+          </div>
+        </div>
+
+        <!-- Step 3: Send Preview -->
+        <div class="glass-effect rounded-2xl shadow-2xl card-hover">
+          <div class="p-6">
+            <div class="flex items-center mb-6">
+              <div
+                class="w-12 h-12 bg-gradient-to-r from-green-500 to-blue-600 rounded-full flex items-center justify-center text-white font-bold text-xl mr-4"
+              >
+                3
+              </div>
+              <h2 class="text-3xl font-bold text-white">
+                <i class="fas fa-chart-line text-green-300 mr-3"></i>
+                Step 3: Review Summary
+              </h2>
+            </div>
+
+            <!-- Statistics -->
+            <div class="grid grid-cols-3 gap-6 mb-8">
+              <div class="stat-card rounded-xl p-6 text-center">
+                <div class="text-4xl font-bold text-white mb-2">
+                  {{ addressList.length }}
+                </div>
+                <div class="text-lg text-purple-200">Addresses</div>
+              </div>
+              <div class="stat-card rounded-xl p-6 text-center">
+                <div class="text-4xl font-bold text-white mb-2">
+                  {{ totalRewardCount }}
+                </div>
+                <div class="text-lg text-purple-200">Rewards</div>
+              </div>
+              <div class="stat-card rounded-xl p-6 text-center">
+                <div class="text-4xl font-bold text-white mb-2">
+                  {{ totalRewardCount * addressList.length }}
+                </div>
+                <div class="text-lg text-purple-200">Total</div>
+              </div>
+            </div>
+
+            <!-- Item Summary -->
+            <div v-if="waitAddRewardList.length > 0">
+              <h3 class="text-xl font-semibold text-white mb-4">
+                Item Summary
+              </h3>
+              <div
+                class="grid grid-cols-1 md:grid-cols-2 gap-4 max-h-60 overflow-y-auto scrollbar-thin"
+              >
+                <div
+                  v-for="reward in waitAddRewardList"
+                  :key="reward.id"
+                  class="flex justify-between items-center p-4 bg-white/10 rounded-lg backdrop-blur-sm"
+                >
+                  <div class="flex items-center space-x-3">
+                    <div class="w-10 h-10 icon-container">
+                      <img
+                        :src="getItemIcon(reward.id)"
+                        :alt="getItemName(reward.id)"
+                        class="w-8 h-8 item-icon"
+                        @error="handleImageError"
+                      />
+                    </div>
+                    <span class="font-medium text-white text-lg"
+                      >{{ getItemName(reward.id) }}</span
+                    >
+                  </div>
+                  <div class="flex items-center space-x-2">
+                    <span class="text-sm text-purple-200"
+                      >{{ reward.count }} × {{ addressList.length }}</span
+                    >
+                    <span class="font-bold text-yellow-300 text-lg"
+                      >= {{ reward.count * addressList.length }}</span
+                    >
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!-- Step 4: Launch Airdrop -->
+        <div class="glass-effect rounded-2xl shadow-2xl card-hover">
+          <div class="p-6">
+            <div class="flex items-center mb-6">
+              <div
+                class="w-12 h-12 bg-gradient-to-r from-red-500 to-orange-600 rounded-full flex items-center justify-center text-white font-bold text-xl mr-4"
+              >
+                4
+              </div>
+              <h2 class="text-3xl font-bold text-white">
+                <i class="fas fa-rocket text-red-300 mr-3"></i>
+                Step 4: Launch Airdrop
+              </h2>
+            </div>
+
+            <div class="text-center">
+              <div class="mb-8">
+                <p class="text-xl text-purple-200 mb-4">
+                  Ready to send rewards to {{ addressList.length }} addresses
+                </p>
+                <p class="text-lg text-purple-300">
+                  Total rewards to distribute: {{ totalRewardCount *
+                  addressList.length }}
+                </p>
+              </div>
+
+              <button
+                class="btn btn-primary btn-xl gradient-bg-3 border-0 text-white shadow-2xl btn-glow"
+                @click="sendAllRewards"
+                :disabled="addressList.length === 0 || waitAddRewardList.length === 0"
+              >
+                <i class="fas fa-rocket mr-3 animate-bounce-slow"></i>
+                Launch Airdrop ({{ totalRewardCount * addressList.length }}
+                rewards)
+              </button>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- Add Reward Modal -->
+      <div class="modal" :class="{ 'modal-open': showAddRewardDialog }">
+        <div
+          class="modal-box w-11/12 max-w-4xl bg-gradient-to-br from-gray-900/95 to-black/95 backdrop-blur-xl border border-white/20 shadow-2xl"
+        >
+          <h3 class="font-bold text-3xl mb-8 text-white text-center">
+            <i class="fas fa-gift text-purple-300 mr-2"></i>
+            Add Reward
+          </h3>
+          <div class="space-y-6 max-h-[80vh] overflow-y-auto">
+            <div class="form-control">
+              <label class="label">
+                <span class="label-text font-semibold text-white text-lg"
+                  >Select Item</span
+                >
+              </label>
+              <div
+                class="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 max-h-96 overflow-y-auto scrollbar-thin"
+              >
+                <div
+                  v-for="item in staticGoods"
+                  :key="item.id"
+                  @click="selectedReward = item.id"
+                  :class="[
+                      'p-4 rounded-xl border-2 cursor-pointer transition-all duration-200 hover:scale-105',
+                      selectedReward == item.id 
+                        ? 'border-purple-400 bg-purple-500/20 shadow-lg shadow-purple-500/30' 
+                        : 'border-white/20 bg-white/5 hover:bg-white/10 hover:border-white/40'
+                    ]"
+                >
+                  <div class="flex flex-col items-center space-y-3 text-center">
+                    <div class="w-16 h-16 icon-container">
+                      <img
+                        :src="getItemIcon(item.id)"
+                        :alt="item.name"
+                        class="w-12 h-12 item-icon"
+                        @error="handleImageError"
+                      />
+                    </div>
+                    <div>
+                      <div class="font-semibold text-white text-base">
+                        {{ item.name }}
+                      </div>
+                      <div class="text-sm text-purple-200 mt-1">
+                        Stock: {{ item.count }}
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div class="form-control">
+              <label class="label">
+                <span class="label-text font-semibold text-white text-lg"
+                  >Quantity per Address</span
+                >
+              </label>
+              <input
+                type="number"
+                v-model="rewardQuantity"
+                min="1"
+                class="input input-bordered w-full bg-white/10 border-white/30 text-white placeholder-white/50 text-lg text-center"
+                placeholder="Enter quantity"
+              />
+            </div>
+          </div>
+          <div class="modal-action">
+            <button
+              class="btn btn-primary btn-lg gradient-bg-2 border-0 text-white"
+              @click="addRewardConfirm"
+              :disabled="!selectedReward || !rewardQuantity || rewardQuantity <= 0"
+            >
+              <i class="fas fa-check mr-2"></i>
+              Add Reward
+            </button>
+            <button class="btn btn-ghost btn-lg text-white" @click="closeModal">
+              Cancel
+            </button>
+          </div>
+        </div>
+        <div
+          class="modal-backdrop bg-black/50 backdrop-blur-sm"
+          @click="closeModal"
+        ></div>
+      </div>
+
+      <!-- Password Verification Modal -->
+      <div class="modal" :class="{ 'modal-open': showPasswordDialog }">
+        <div
+          class="modal-box max-w-md bg-gradient-to-br from-gray-900/95 to-black/95 backdrop-blur-xl border border-white/20 shadow-2xl"
+        >
+          <h3 class="font-bold text-2xl mb-6 text-white text-center">
+            <i class="fas fa-shield-alt text-red-300 mr-2"></i>
+            Final Confirmation
+          </h3>
+          <div class="space-y-6">
+            <div class="text-center">
+              <div class="text-6xl mb-4 animate-bounce-slow">🔐</div>
+              <p class="text-purple-200 text-lg mb-2">
+                Confirm Airdrop Operation
+              </p>
+              <p class="text-purple-300 text-sm">
+                This action will send {{ totalRewardCount * addressList.length
+                }} rewards to {{ addressList.length }} addresses
+              </p>
+            </div>
+
+            <div class="grid grid-cols-1 gap-6">
+              <div class="form-control">
+                <label class="label">
+                  <span class="label-text font-semibold text-white text-lg"
+                    ><i class="fas fa-tag text-blue-300 mr-2"></i>Activity
+                    Name</span
+                  >
+                </label>
+                <input
+                  type="text"
+                  v-model="reason"
+                  class="input input-bordered w-full bg-white/10 border-white/30 text-white placeholder-white/50 text-lg text-center"
+                  placeholder="Enter activity name (e.g., Spring Festival Event)"
+                  @keyup.enter="verifyPassword"
+                />
+                <label class="label">
+                  <span class="label-text-alt text-purple-200"
+                    >This will be used to identify the airdrop activity</span
+                  >
+                </label>
+              </div>
+
+              <div class="form-control">
+                <label class="label">
+                  <span class="label-text font-semibold text-white text-lg"
+                    ><i class="fas fa-lock text-red-300 mr-2"></i>Admin
+                    Password</span
+                  >
+                </label>
+                <input
+                  type="password"
+                  v-model="password"
+                  class="input input-bordered w-full bg-white/10 border-white/30 text-white placeholder-white/50 text-lg text-center"
+                  placeholder="Enter password"
+                  @keyup.enter="verifyPassword"
+                />
+                <label class="label">
+                  <span class="label-text-alt text-purple-200"
+                    >Press Enter to confirm</span
+                  >
+                </label>
+              </div>
+            </div>
+          </div>
+          <div class="modal-action">
+            <button
+              class="btn btn-primary btn-lg gradient-bg-3 border-0 text-white"
+              @click="verifyPassword"
+              :disabled="!password.trim() || !reason.trim() || isVerifying"
+            >
+              <i v-if="isVerifying" class="fas fa-spinner fa-spin mr-2"></i>
+              <i v-else class="fas fa-rocket mr-2"></i>
+              {{ isVerifying ? 'Verifying...' : 'Launch Airdrop' }}
+            </button>
+            <button
+              class="btn btn-ghost btn-lg text-white"
+              @click="closePasswordModal"
+            >
+              Cancel
+            </button>
+          </div>
+        </div>
+        <div
+          class="modal-backdrop bg-black/50 backdrop-blur-sm"
+          @click="closePasswordModal"
+        ></div>
+      </div>
+
+      <!-- History Modal -->
+      <div class="modal" :class="{ 'modal-open': showHistoryDialog }">
+        <div
+          class="modal-box w-11/12 max-w-5xl bg-gradient-to-br from-gray-900/95 to-black/95 backdrop-blur-xl border border-white/20 shadow-2xl"
+        >
+          <div class="flex justify-between items-center mb-4">
+            <h3 class="font-bold text-2xl text-white">
+              <i class="fas fa-history text-blue-300 mr-2"></i>
+              Airdrop History
+            </h3>
+            <button
+              class="btn btn-circle btn-sm bg-white/20 border-0 hover:bg-white/30"
+              @click="closeHistoryModal"
+            >
+              <i class="fas fa-times text-white"></i>
+            </button>
+          </div>
+
+          <!-- Loading State -->
+          <div v-if="isLoadingHistory" class="text-center py-8">
+            <div class="text-4xl mb-2 animate-bounce-slow">⏳</div>
+            <p class="text-purple-200">Loading history...</p>
+          </div>
+
+          <!-- History Content -->
+          <div
+            v-else-if="historyData.length > 0"
+            class="space-y-3 max-h-[75vh] overflow-y-auto"
+          >
+            <div
+              v-for="item in historyData"
+              :key="item.id"
+              class="bg-white/10 rounded-lg p-4 backdrop-blur-sm border border-white/20"
+            >
+              <!-- Header Row -->
+              <div class="flex justify-between items-center mb-3">
+                <div class="flex items-center space-x-3">
+                  <h4 class="text-lg font-bold text-white">
+                    {{ item.reason }}
+                  </h4>
+                  <div
+                    :class="[
+                      'badge badge-xs',
+                      item.status >=0 ? 'badge-success' : 'badge-error'
+                    ]"
+                  >
+                    {{ item.status >=0 ? '✓' : '✗' }}
+                  </div>
+                </div>
+                <div
+                  class="flex items-center space-x-3 text-xs text-purple-300"
+                >
+                  <span>
+                    <i class="fas fa-calendar mr-1"></i>
+                    {{ formatDate(item.createTime) }}
+                  </span>
+                  <span class="text-purple-400">#{{ item.id.slice(-8) }}</span>
+                </div>
+              </div>
+
+              <!-- Rewards List - Compact -->
+              <div class="flex flex-wrap gap-2">
+                <div
+                  v-for="reward in parseGoodList(item.goodList)"
+                  :key="reward.id"
+                  class="flex items-center space-x-2 px-3 py-2 bg-white/10 rounded-lg border border-white/20"
+                >
+                  <div class="w-6 h-6 icon-container">
+                    <img
+                      :src="getItemIcon(reward.id)"
+                      :alt="getItemName(reward.id)"
+                      class="w-5 h-5 item-icon"
+                      @error="handleImageError"
+                    />
+                  </div>
+                  <span class="text-sm font-medium text-white">
+                    {{ getItemName(reward.id) }}
+                  </span>
+                  <span
+                    class="text-xs text-purple-200 bg-purple-500/20 px-2 py-1 rounded"
+                  >
+                    ×{{ reward.count }}
+                  </span>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <!-- Empty State -->
+          <div v-else class="text-center py-8">
+            <div class="text-6xl mb-3 animate-bounce-slow">📜</div>
+            <p class="text-purple-200">No history found</p>
+            <p class="text-purple-300 text-sm mt-1">
+              Start your first airdrop to see history here
+            </p>
+          </div>
+
+          <!-- Pagination - Compact -->
+          <div
+            v-if="historyData.length > 0"
+            class="flex justify-center items-center space-x-3 mt-4 pt-3 border-t border-white/20"
+          >
+            <button
+              class="btn btn-sm btn-outline border-white/30 text-white hover:bg-white/10"
+              @click="changePage(currentPage - 1)"
+              :disabled="currentPage <= 1"
+            >
+              <i class="fas fa-chevron-left"></i>
+            </button>
+
+            <div class="flex items-center space-x-2">
+              <span class="text-sm text-white">Page</span>
+              <input
+                type="number"
+                v-model.number="currentPage"
+                min="1"
+                class="input input-bordered input-xs w-16 text-center bg-white/10 border-white/30 text-white"
+                @change="loadHistory"
+              />
+              <span class="text-sm text-white">/ {{ totalPages }}</span>
+            </div>
+
+            <button
+              class="btn btn-sm btn-outline border-white/30 text-white hover:bg-white/10"
+              @click="changePage(currentPage + 1)"
+              :disabled="currentPage >= totalPages"
+            >
+              <i class="fas fa-chevron-right"></i>
+            </button>
+          </div>
+        </div>
+        <div
+          class="modal-backdrop bg-black/50 backdrop-blur-sm"
+          @click="closeHistoryModal"
+        ></div>
+      </div>
+
+      <!-- Toast Container -->
+      <div class="fixed top-4 right-4 z-50">
+        <div
+          v-if="toast.show"
+          :class="['alert', toast.type, 'glass-effect', 'shadow-2xl', 'min-w-80', 'max-w-md', 'animate-slide-in']"
+        >
+          <div class="flex items-center justify-between w-full">
+            <div class="flex items-center">
+              <i
+                v-if="toast.type === 'alert-success'"
+                class="fas fa-check-circle text-green-400 mr-3"
+              ></i>
+              <i
+                v-else-if="toast.type === 'alert-error'"
+                class="fas fa-exclamation-circle text-red-400 mr-3"
+              ></i>
+              <i v-else class="fas fa-info-circle text-blue-400 mr-3"></i>
+              <span class="text-white font-medium">{{ toast.message }}</span>
+            </div>
+            <button
+              @click="toast.show = false"
+              class="btn btn-circle btn-sm bg-white/20 border-0 hover:bg-white/30 ml-3"
+            >
+              <i class="fas fa-times text-white"></i>
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <script>
+      const { createApp } = Vue;
+      const API_BASE_URL = "https://zombies.telgather.com/platform";
+
+      const Toast = {
+        template: `
+        <div class="fixed top-4 right-4 z-50">
+          <div v-if="show" :class="['alert', type, 'glass-effect', 'shadow-2xl', 'min-w-80', 'max-w-md', 'animate-slide-in']">
+            <div class="flex items-center justify-between w-full">
+              <div class="flex items-center">
+                <i v-if="type === 'alert-success'" class="fas fa-check-circle text-green-400 mr-3"></i>
+                <i v-else-if="type === 'alert-error'" class="fas fa-exclamation-circle text-red-400 mr-3"></i>
+                <i v-else class="fas fa-info-circle text-blue-400 mr-3"></i>
+                <span class="text-white font-medium">{{ message }}</span>
+              </div>
+              <button
+                @click="$emit('close')"
+                class="btn btn-circle btn-sm bg-white/20 border-0 hover:bg-white/30 ml-3"
+              >
+                <i class="fas fa-times text-white"></i>
+              </button>
+            </div>
+          </div>
+        </div>
+      `,
+        props: ["show", "type", "message"],
+        emits: ["close"],
+      };
+
+      createApp({
+        data() {
+          return {
+            reason: "",
+            waitInputAddresses: "",
+            addressList: [],
+            waitAddRewardList: [],
+            showAddRewardDialog: false,
+            showPasswordDialog: false,
+            selectedReward: "",
+            rewardQuantity: "",
+            password: "",
+            isVerifying: false,
+            searchAddress: "",
+            staticGoods: [],
+            toast: {
+              show: false,
+              type: "alert-info",
+              message: "",
+            },
+            showHistoryDialog: false,
+            historyData: [],
+            currentPage: 1,
+            totalPages: 1,
+            isLoadingHistory: false,
+          };
+        },
+        computed: {
+          totalRewardCount() {
+            return this.waitAddRewardList.reduce(
+              (total, reward) => total + reward.count,
+              0
+            );
+          },
+          filteredAddresses() {
+            if (!this.searchAddress) return this.addressList;
+            return this.addressList.filter((addr) =>
+              addr.toLowerCase().includes(this.searchAddress.toLowerCase())
+            );
+          },
+        },
+        components: {
+          Toast,
+        },
+        watch: {
+          showHistoryDialog(newVal) {
+            if (newVal) {
+              this.loadHistory();
+            }
+          },
+        },
+        methods: {
+          // Parse addresses
+          parseAddresses() {
+            if (this.waitInputAddresses.trim()) {
+              const addresses = this.waitInputAddresses
+                .split("\n")
+                .map((addr) => addr.trim())
+                .filter((addr) => addr && addr.length > 0);
+              this.addressList = addresses;
+            } else {
+              this.addressList = [];
+            }
+          },
+
+          // Remove address
+          removeAddress(index) {
+            this.addressList.splice(index, 1);
+            this.updateAddressInput();
+            this.showToast(`Address removed`, "alert-info");
+          },
+
+          // Update address input
+          updateAddressInput() {
+            this.waitInputAddresses = this.addressList.join("\n");
+          },
+
+          // Clear addresses
+          clearAddresses() {
+            this.addressList = [];
+            this.waitInputAddresses = "";
+            this.showToast("All addresses cleared", "alert-info");
+          },
+
+          // Export addresses
+          exportAddresses() {
+            if (this.addressList.length === 0) {
+              this.showToast("No addresses to export", "alert-error");
+              return;
+            }
+            const text = this.addressList.join("\n");
+            const blob = new Blob([text], { type: "text/plain" });
+            const url = URL.createObjectURL(blob);
+            const a = document.createElement("a");
+            a.href = url;
+            a.download = `addresses_${
+              new Date().toISOString().split("T")[0]
+            }.txt`;
+            a.click();
+            URL.revokeObjectURL(url);
+            this.showToast(
+              `Exported ${this.addressList.length} addresses`,
+              "alert-success"
+            );
+          },
+
+          // Import addresses
+          importAddresses() {
+            const input = document.createElement("input");
+            input.type = "file";
+            input.accept = ".txt,.csv";
+            input.onchange = (e) => {
+              const file = e.target.files[0];
+              if (file) {
+                const reader = new FileReader();
+                reader.onload = (e) => {
+                  const content = e.target.result;
+                  this.waitInputAddresses = content;
+                  this.parseAddresses();
+                  this.showToast(
+                    `Imported ${this.addressList.length} addresses`,
+                    "alert-success"
+                  );
+                };
+                reader.readAsText(file);
+              }
+            };
+            input.click();
+          },
+
+          // Add reward confirmation
+          addRewardConfirm() {
+            if (
+              this.selectedReward &&
+              this.rewardQuantity &&
+              this.rewardQuantity > 0
+            ) {
+              const itemId = parseInt(this.selectedReward);
+              const count = parseInt(this.rewardQuantity);
+
+              // Check if item already exists
+              const existingIndex = this.waitAddRewardList.findIndex(
+                (item) => item.id === itemId
+              );
+
+              if (existingIndex > -1) {
+                // If exists, increase quantity
+                this.waitAddRewardList[existingIndex].count += count;
+                this.showToast(
+                  `Added ${count} more ${this.getItemName(itemId)}`,
+                  "alert-success"
+                );
+              } else {
+                // If not exists, add new item
+                this.waitAddRewardList.push({
+                  id: itemId,
+                  count: count,
+                });
+                this.showToast(
+                  `Added ${count} ${this.getItemName(itemId)}`,
+                  "alert-success"
+                );
+              }
+
+              this.closeModal();
+            }
+          },
+
+          // Update reward count
+          updateRewardCount(id, count) {
+            const reward = this.waitAddRewardList.find(
+              (item) => item.id === id
+            );
+            if (reward && count > 0) {
+              reward.count = parseInt(count);
+              this.showToast(
+                `Updated ${this.getItemName(id)} quantity to ${count}`,
+                "alert-info"
+              );
+            }
+          },
+
+          // Remove reward
+          removeWaitReward(id) {
+            const index = this.waitAddRewardList.findIndex(
+              (item) => item.id === id
+            );
+            if (index > -1) {
+              const removedReward = this.waitAddRewardList.splice(index, 1)[0];
+              this.showToast(
+                `Removed ${removedReward.count} ${this.getItemName(id)}`,
+                "alert-info"
+              );
+            }
+          },
+
+          // Get item name
+          getItemName(id) {
+            const item = this.staticGoods.find((item) => item.id == id);
+            return item ? item.name : `Unknown Item (${id})`;
+          },
+
+          // Get item icon
+          getItemIcon(id) {
+            const item = this.staticGoods.find((item) => item.id == id);
+            if (item && item.icon) {
+              const iconPath = `imgs/${item.icon}.png`;
+              console.log(`Loading icon for item ${id}: ${iconPath}`);
+              return iconPath;
+            }
+            console.log(`No icon found for item ${id}, using default`);
+            return "imgs/default_icon.png";
+          },
+
+          // Handle image error
+          handleImageError(event) {
+            console.log("Image load error:", event.target.src);
+            // Replace with fallback icon
+            const parent = event.target.parentElement;
+            if (parent) {
+              parent.innerHTML =
+                '<i class="fas fa-question text-white/50 text-xl"></i>';
+            }
+          },
+
+          // Send all rewards
+          sendAllRewards() {
+            if (this.addressList.length === 0) {
+              this.showToast(
+                "Please add wallet addresses first",
+                "alert-error"
+              );
+              return;
+            }
+
+            if (this.waitAddRewardList.length === 0) {
+              this.showToast("Please add rewards first", "alert-error");
+              return;
+            }
+
+            // Show password verification dialog
+            this.showPasswordDialog = true;
+            this.password = "";
+            this.reason = "";
+          },
+
+          // Verify password and proceed with airdrop
+          async verifyPassword() {
+            if (!this.password.trim()) {
+              this.showToast("Please enter password", "alert-error");
+              return;
+            }
+            if (!this.reason.trim()) {
+              this.showToast("Please enter reason", "alert-error");
+              return;
+            }
+
+            this.isVerifying = true;
+
+            let goodList = [];
+            for (const item of this.waitAddRewardList) {
+              goodList.push({
+                id: item.id,
+                count: item.count,
+              });
+            }
+            try {
+              // Build send data
+              const sendData = {
+                reason: this.reason,
+                walletList: this.addressList,
+                goodList: goodList,
+                password: this.password,
+              };
+
+              console.log("Sending data to server:", sendData);
+
+              this.showToast(
+                `Verifying password and preparing airdrop...`,
+                "alert-info"
+              );
+
+              // Call server API for password verification and airdrop
+              const response = await fetch(
+                `${API_BASE_URL}/pog-service/callback/add/good`,
+                {
+                  method: "POST",
+                  headers: {
+                    "Content-Type": "application/json",
+                  },
+                  body: JSON.stringify(sendData),
+                }
+              );
+
+              const result = await response.json();
+
+              if (response.ok) {
+                this.closePasswordModal();
+                this.showToast(
+                  "Airdrop completed successfully!",
+                  "alert-success"
+                );
+                console.log("Airdrop result:", result);
+              } else {
+                this.showToast(
+                  result.message ||
+                    "Airdrop failed. Please check your password and try again.",
+                  "alert-error"
+                );
+                this.password = "";
+              }
+            } catch (error) {
+              console.error("Airdrop error:", error);
+              this.showToast(
+                "Network error. Please check your connection and try again.",
+                "alert-error"
+              );
+              this.password = "";
+            } finally {
+              this.isVerifying = false;
+            }
+          },
+
+          // Close password modal
+          closePasswordModal() {
+            this.showPasswordDialog = false;
+            this.password = "";
+            this.reason = "";
+            this.isVerifying = false;
+          },
+
+          closeModal() {
+            this.showAddRewardDialog = false;
+            this.selectedReward = "";
+            this.rewardQuantity = "";
+          },
+
+          showToast(message, type = "alert-info") {
+            // Hide existing toast first
+            this.toast.show = false;
+
+            // Show new toast after a brief delay
+            setTimeout(() => {
+              this.toast = {
+                show: true,
+                type: type,
+                message: message,
+              };
+
+              // Auto hide after 4 seconds
+              setTimeout(() => {
+                this.toast.show = false;
+              }, 4000);
+            }, 100);
+          },
+
+          // Load history data
+          async loadHistory() {
+            this.isLoadingHistory = true;
+            try {
+              const response = await fetch(
+                `${API_BASE_URL}/pog-service/callback/add/good/page?page=${this.currentPage}`,
+                {
+                  method: "GET",
+                  headers: {
+                    "Content-Type": "application/json",
+                  },
+                }
+              );
+
+              const result = await response.json();
+
+              if (response.ok && result.success) {
+                this.historyData = result.data.content || [];
+                // Assuming the API returns total pages or we can calculate it
+                // For now, we'll set a default total pages
+                this.totalPages = Math.max(
+                  1,
+                  Math.ceil(this.historyData.length / 10)
+                );
+                this.showToast("History loaded successfully", "alert-success");
+              } else {
+                this.showToast(
+                  result.message || "Failed to load history",
+                  "alert-error"
+                );
+              }
+            } catch (error) {
+              console.error("History loading error:", error);
+              this.showToast(
+                "Network error. Please check your connection and try again.",
+                "alert-error"
+              );
+            } finally {
+              this.isLoadingHistory = false;
+            }
+          },
+
+          // Change page
+          changePage(newPage) {
+            if (newPage >= 1 && newPage <= this.totalPages) {
+              this.currentPage = newPage;
+              this.loadHistory();
+            }
+          },
+
+          // Parse good list from JSON string
+          parseGoodList(goodListString) {
+            try {
+              return JSON.parse(goodListString);
+            } catch (error) {
+              console.error("Error parsing good list:", error);
+              return [];
+            }
+          },
+
+          // Format date - Display UTC time directly
+          formatDate(dateString) {
+            try {
+              const date = new Date(dateString);
+              // Format as UTC time: YYYY-MM-DD HH:mm:ss
+              const year = date.getUTCFullYear();
+              const month = String(date.getUTCMonth() + 1).padStart(2, "0");
+              const day = String(date.getUTCDate()).padStart(2, "0");
+              const hours = String(date.getUTCHours()).padStart(2, "0");
+              const minutes = String(date.getUTCMinutes()).padStart(2, "0");
+              const seconds = String(date.getUTCSeconds()).padStart(2, "0");
+
+              return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} UTC`;
+            } catch (error) {
+              return dateString;
+            }
+          },
+
+          // Close history modal
+          closeHistoryModal() {
+            this.showHistoryDialog = false;
+            this.historyData = [];
+            this.currentPage = 1;
+            this.totalPages = 1;
+          },
+        },
+
+        mounted() {
+          const m = {
+            1: {
+              name: "POG",
+              count: 100,
+              icon: "icon_pog",
+            },
+            2: {
+              name: "Diamond",
+              count: 100,
+              icon: "icon_gem",
+            },
+            3: {
+              name: "GAME_SHARD",
+              count: 100,
+              icon: "icon_puzzle",
+            },
+            5: {
+              name: "CRIT_CARD",
+              count: 100,
+              icon: "icon_crit",
+            },
+            6: {
+              name: "FREE_ITEM_BOX",
+              count: 100,
+              icon: "icon_item_box",
+            },
+            7: {
+              name: "ITEM_BOX",
+              count: 100,
+              icon: "icon_item_box",
+            },
+            8: {
+              name: "FREE_POG_BOX",
+              count: 100,
+              icon: "icon_pog_box",
+            },
+            9: {
+              name: "POG_BOX",
+              count: 100,
+              icon: "icon_pog_box",
+            },
+          };
+          this.staticGoods = [];
+          for (const key in m) {
+            this.staticGoods.push({
+              id: key,
+              name: m[key].name,
+              count: m[key].count,
+              icon: m[key].icon,
+            });
+          }
+
+          console.log(this.staticGoods);
+        },
+      }).mount("#app");
+    </script>
+  </body>
+</html>

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio