权益首页活动.html 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  6. <title>权益专区</title>
  7. <script src="https://cdn.tailwindcss.com"></script>
  8. <style>
  9. .no-scrollbar::-webkit-scrollbar { display: none; }
  10. .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
  11. .tab-indicator { transition: all 0.3s ease; }
  12. .view-transition { transition: transform 0.35s cubic-bezier(0.4, 0.0, 0.2, 1); }
  13. .pb-safe { padding-bottom: env(safe-area-inset-bottom); }
  14. </style>
  15. </head>
  16. <body class="bg-gray-100 flex justify-center h-screen overflow-hidden font-sans">
  17. <div class="fixed top-[15%] right-0 z-[100] flex flex-col items-end gap-3 max-w-sm pointer-events-none">
  18. <!-- 等级控制 (全局显示) -->
  19. <div class="relative group pointer-events-auto">
  20. <button onclick="toggleSimPanel()" class="bg-black/70 text-white text-[11px] py-1.5 px-2 rounded-l-md shadow-md flex items-center gap-1 backdrop-blur-sm">
  21. <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
  22. 等级
  23. </button>
  24. <div id="sim-panel" class="absolute top-0 right-[110%] bg-white/95 backdrop-blur-md border border-gray-200 p-2 rounded-lg shadow-xl w-[140px] text-[11px] transition-all origin-right scale-0 opacity-0 pointer-events-none">
  25. <div class="font-bold mb-1.5 text-gray-800 border-b pb-1 text-center">切换等级</div>
  26. <div class="flex flex-col gap-1.5">
  27. <button onclick="setUserLevel('prospect')" id="btn-state-prospect" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">潜客</button>
  28. <button onclick="setUserLevel('credit_only')" id="btn-state-credit_only" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">仅持有信用卡</button>
  29. <button onclick="setUserLevel('advance')" id="btn-state-advance" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">运筹理财</button>
  30. <button onclick="setUserLevel('premier')" id="btn-state-premier" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">卓越理财</button>
  31. <button onclick="setUserLevel('elite')" id="btn-state-elite" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">卓越理财·尊尚</button>
  32. <button onclick="setUserLevel('global')" id="btn-state-global" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">环球私人银行</button>
  33. </div>
  34. </div>
  35. </div>
  36. <!-- 数据控制 (默认隐藏,仅在全部活动页显示) -->
  37. <div id="data-control-wrapper" class="relative group hidden pointer-events-auto">
  38. <button onclick="toggleDataPanel()" class="bg-blue-600/80 text-white text-[11px] py-1.5 px-2 rounded-l-md shadow-md flex items-center gap-1 backdrop-blur-sm">
  39. <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path></svg>
  40. 数据
  41. </button>
  42. <div id="data-panel" class="absolute top-0 right-[110%] bg-white/95 backdrop-blur-md border border-gray-200 p-2 rounded-lg shadow-xl w-[120px] text-[11px] transition-all origin-right scale-0 opacity-0 pointer-events-none">
  43. <div class="font-bold mb-1.5 text-gray-800 border-b pb-1 text-center">活动数据</div>
  44. <div class="flex flex-col gap-1.5">
  45. <button onclick="setAllActivitiesData(true)" id="btn-data-true" class="px-2 py-1.5 rounded bg-[#d70014] text-white transition font-medium">有数据</button>
  46. <button onclick="setAllActivitiesData(false)" id="btn-data-false" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">无数据</button>
  47. </div>
  48. </div>
  49. </div>
  50. <!-- 详情页控制 (默认隐藏,仅在活动详情页显示) -->
  51. <div id="detail-control-wrapper" class="hidden flex-col gap-3 pointer-events-auto">
  52. <!-- 按钮状态控制 -->
  53. <div class="relative group">
  54. <button onclick="toggleDetailBtnPanel()" class="bg-indigo-600/80 text-white text-[11px] py-1.5 px-2 rounded-l-md shadow-md flex items-center gap-1 backdrop-blur-sm">
  55. <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="9" x2="15" y2="15"></line><line x1="15" y1="9" x2="9" y2="15"></line></svg>
  56. 按钮
  57. </button>
  58. <div id="detail-btn-panel" class="absolute top-0 right-[110%] bg-white/95 backdrop-blur-md border border-gray-200 p-2 rounded-lg shadow-xl w-[120px] text-[11px] transition-all origin-right scale-0 opacity-0 pointer-events-none">
  59. <div class="font-bold mb-1.5 text-gray-800 border-b pb-1 text-center">按钮状态</div>
  60. <div class="flex flex-col gap-1.5">
  61. <button onclick="setDetailBtnState('enroll')" id="btn-detail-enroll" class="px-2 py-1.5 rounded bg-[#d70014] text-white transition font-medium">报名</button>
  62. <button onclick="setDetailBtnState('progress')" id="btn-detail-progress" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">查看进度</button>
  63. <button onclick="setDetailBtnState('benefits')" id="btn-detail-benefits" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">查看权益</button>
  64. <button onclick="setDetailBtnState('more')" id="btn-detail-more" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">查看更多</button>
  65. </div>
  66. </div>
  67. </div>
  68. <!-- 报名结果控制 (仅报名状态下显示) -->
  69. <div class="relative group" id="enroll-result-control">
  70. <button onclick="toggleEnrollResultPanel()" class="bg-purple-600/80 text-white text-[11px] py-1.5 px-2 rounded-l-md shadow-md flex items-center gap-1 backdrop-blur-sm">
  71. <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
  72. 结果
  73. </button>
  74. <div id="enroll-result-panel" class="absolute top-0 right-[110%] bg-white/95 backdrop-blur-md border border-gray-200 p-2 rounded-lg shadow-xl w-[120px] text-[11px] transition-all origin-right scale-0 opacity-0 pointer-events-none">
  75. <div class="font-bold mb-1.5 text-gray-800 border-b pb-1 text-center">报名结果</div>
  76. <div class="flex flex-col gap-1.5">
  77. <button onclick="setEnrollCondition('success')" id="cond-success" class="px-2 py-1.5 rounded bg-[#d70014] text-white transition font-medium">通过校验</button>
  78. <button onclick="setEnrollCondition('not_eligible')" id="cond-not_eligible" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">不符合</button>
  79. <button onclick="setEnrollCondition('full')" id="cond-full" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">名额不足</button>
  80. <button onclick="setEnrollCondition('exclusive')" id="cond-exclusive" class="px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition">命中互斥</button>
  81. </div>
  82. </div>
  83. </div>
  84. </div>
  85. </div>
  86. <div class="w-full max-w-[400px] h-full bg-white flex flex-col relative shadow-2xl overflow-hidden">
  87. <!-- 顶部状态栏 (固定) -->
  88. <div class="h-11 w-full flex justify-between items-center px-5 text-black bg-white z-[90] relative shrink-0">
  89. <span class="text-[15px] font-medium">9:41</span>
  90. <div class="flex gap-1.5 items-center">
  91. <svg width="18" height="12" viewBox="0 0 18 12" fill="none"><path d="M1.5 12V9H3.5V12H1.5ZM6 12V6.5H8V12H6ZM10.5 12V3.5H12.5V12H10.5ZM15 12V0H17V12H15Z" fill="black"/></svg>
  92. <svg width="16" height="12" viewBox="0 0 16 12" fill="none"><path d="M8 12C9.10457 12 10 11.1046 10 10C10 8.89543 9.10457 8 8 8C6.89543 8 6 8.89543 6 10C6 11.1046 6.89543 12 8 12Z" fill="black"/><path d="M2.34315 5.65685C5.46734 2.53266 10.5327 2.53266 13.6569 5.65685" stroke="black" stroke-width="1.5" stroke-linecap="round"/><path d="M5.17157 8.48528C6.73367 6.92318 9.26633 6.92318 10.8284 8.48528" stroke="black" stroke-width="1.5" stroke-linecap="round"/></svg>
  93. <svg width="25" height="12" viewBox="0 0 25 12" fill="none"><rect x="0.5" y="0.5" width="21" height="11" rx="2.5" stroke="black"/><rect x="2" y="2" width="18" height="8" rx="1" fill="black"/><path d="M23 4V8" stroke="black" stroke-width="1.5" stroke-linecap="round"/></svg>
  94. </div>
  95. </div>
  96. <!-- 视图层容器 (包含所有子页面) -->
  97. <div class="flex-1 relative w-full overflow-hidden bg-gray-100">
  98. <div id="home-view" class="absolute inset-0 bg-white flex flex-col view-transition translate-x-0 z-10">
  99. <header class="h-11 flex items-center justify-between px-4 bg-white z-20 shrink-0">
  100. <button class="text-gray-800 p-1"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg></button>
  101. <h1 class="text-[17px] font-medium text-gray-900 absolute left-1/2 -translate-x-1/2">权益介绍页</h1>
  102. <div class="w-8"></div>
  103. </header>
  104. <div class="flex-1 overflow-y-auto no-scrollbar pb-8">
  105. <!-- 潜客横幅 -->
  106. <div id="no-tier-banner" onclick="showZone()" class="px-4 py-3 bg-[#fdfaf8] border-b border-gray-100 flex items-center justify-between cursor-pointer active:bg-gray-50 transition">
  107. <div class="flex items-center gap-3">
  108. <div class="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center border border-gray-200">
  109. <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="14" x="3" y="8" rx="2"/><path d="M12 5a3 3 0 1 0-3 3"/><path d="M15 8a3 3 0 1 0-3-3"/><path d="M12 8v10"/><path d="M3 12h18"/></svg>
  110. </div>
  111. <div>
  112. <div class="text-[15px] text-gray-900">权益首页</div>
  113. <div class="text-[12px] text-gray-500">一键查看您的所有权益</div>
  114. </div>
  115. </div>
  116. <button class="text-[13px] text-gray-500 flex items-center pointer-events-none">
  117. 立即查看 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
  118. </button>
  119. </div>
  120. <!-- Tabs -->
  121. <div class="flex justify-between px-2 pt-2 border-b border-gray-200 relative">
  122. <button onclick="switchTab('premier')" class="flex-1 py-3 text-[14px] font-medium relative text-gray-900" id="tab-btn-premier">卓越理财</button>
  123. <button onclick="switchTab('elite')" class="flex-1 py-3 text-[14px] font-medium relative text-gray-500" id="tab-btn-elite">卓越理财·尊尚</button>
  124. <button onclick="switchTab('global')" class="flex-1 py-3 text-[14px] font-medium relative text-gray-500" id="tab-btn-global">环球私人银行</button>
  125. <div id="tab-indicator" class="absolute bottom-0 h-[2px] bg-[#d70014] tab-indicator" style="width: 33.33%; left: 0%;"></div>
  126. </div>
  127. <!-- 动态内容区 -->
  128. <div id="content-area" class="px-5 pt-5 pb-10"></div>
  129. </div>
  130. </div>
  131. <div id="zone-view" class="absolute inset-0 bg-[#f4f5f8] flex flex-col view-transition translate-x-full z-20">
  132. <header class="h-11 flex items-center justify-between px-4 bg-white z-20 shrink-0 border-b border-gray-100">
  133. <button onclick="hideZone()" class="text-gray-800 p-1"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg></button>
  134. <h1 class="text-[17px] font-medium text-gray-900 absolute left-1/2 -translate-x-1/2">权益首页</h1>
  135. <div class="w-8"></div>
  136. </header>
  137. <div class="flex-1 overflow-y-auto no-scrollbar pb-8" id="zone-content-area"></div>
  138. </div>
  139. <div id="my-activities-view" class="absolute inset-0 bg-white flex flex-col view-transition translate-x-full z-30">
  140. <header class="h-11 flex items-center justify-between px-4 bg-white z-20 shrink-0">
  141. <button onclick="closeMyActivities()" class="text-gray-800 p-1"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg></button>
  142. <h1 class="text-[17px] font-medium text-gray-900 absolute left-1/2 -translate-x-1/2">我的活动</h1>
  143. <div class="w-8"></div>
  144. </header>
  145. <!-- My Activities Tabs -->
  146. <div class="flex border-b border-gray-200 shrink-0 relative">
  147. <button class="flex-1 py-3 text-[15px] font-medium text-gray-900 text-center" onclick="switchActivitiesTab('ongoing')" id="tab-my-ongoing">进行中</button>
  148. <button class="flex-1 py-3 text-[15px] font-medium text-gray-500 text-center" onclick="switchActivitiesTab('ended')" id="tab-my-ended">已结束</button>
  149. <div id="my-activities-indicator" class="absolute bottom-0 h-[2px] bg-[#d70014] transition-all duration-300" style="width: 50%; left: 0%;"></div>
  150. </div>
  151. <div class="flex-1 overflow-y-auto no-scrollbar bg-white">
  152. <!-- 进行中列表 -->
  153. <div id="activities-ongoing-content" class="bg-white">
  154. <div class="flex items-center justify-between py-4 px-5 border-b border-gray-100 cursor-pointer active:bg-gray-50 transition" onclick="openActivityDetail('my_activity')">
  155. <div class="flex items-center gap-4 w-full pr-2">
  156. <img src="https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=100&h=100&fit=crop" class="w-14 h-14 object-cover shrink-0">
  157. <div class="flex-1 overflow-hidden">
  158. <h4 class="text-[14px] text-gray-900 font-medium truncate mb-1">2026 汇丰母亲节特别活动</h4>
  159. <div class="text-[12px] text-gray-400">2026年/9月20日 周六14:00</div>
  160. </div>
  161. </div>
  162. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5"><path d="m9 18 6-6-6-6"/></svg>
  163. </div>
  164. <div class="flex items-center justify-between py-4 px-5 border-b border-gray-100 cursor-pointer active:bg-gray-50 transition" onclick="openActivityDetail('my_activity')">
  165. <div class="flex items-center gap-4 w-full pr-2">
  166. <img src="https://images.unsplash.com/photo-1563986768494-4dee2763ff3f?w=100&h=100&fit=crop" class="w-14 h-14 object-cover shrink-0">
  167. <div class="flex-1 overflow-hidden">
  168. <h4 class="text-[14px] text-gray-900 font-medium truncate mb-1">2026 汇丰中国理财讲座</h4>
  169. <div class="text-[12px] text-gray-400">2026年/9月20日 周六14:00</div>
  170. </div>
  171. </div>
  172. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5"><path d="m9 18 6-6-6-6"/></svg>
  173. </div>
  174. <!-- 信用卡活动专区入口 -->
  175. <div class="flex items-center justify-between py-4 px-5 border-b border-gray-100 cursor-pointer active:bg-gray-50 transition mt-2">
  176. <div class="flex items-center gap-4 w-full pr-2">
  177. <div class="w-14 h-14 bg-gray-200 shrink-0"></div>
  178. <h4 class="text-[15px] text-gray-900 font-medium truncate flex-1">信用卡活动</h4>
  179. </div>
  180. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5"><path d="m9 18 6-6-6-6"/></svg>
  181. </div>
  182. <div class="px-5 py-4 text-[12px] text-gray-500 leading-relaxed">
  183. 温馨提示:如需查看信用卡活动进度,请点击进行中-信用卡活动专区。
  184. </div>
  185. </div>
  186. <!-- 已结束列表 (Grayscale) -->
  187. <div id="activities-ended-content" class="bg-white hidden">
  188. <div class="flex items-center justify-between py-4 px-5 border-b border-gray-100 cursor-pointer opacity-40 grayscale transition" onclick="openActivityDetail('my_activity')">
  189. <div class="flex items-center gap-4 w-full pr-2">
  190. <img src="https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=100&h=100&fit=crop" class="w-14 h-14 object-cover shrink-0">
  191. <div class="flex-1 overflow-hidden">
  192. <h4 class="text-[14px] text-gray-900 font-medium truncate mb-1">2026 汇丰母亲节特别活动</h4>
  193. <div class="text-[12px] text-gray-400">2026年/9月20日 周六14:00</div>
  194. </div>
  195. </div>
  196. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5"><path d="m9 18 6-6-6-6"/></svg>
  197. </div>
  198. <div class="flex items-center justify-between py-4 px-5 border-b border-gray-100 cursor-pointer opacity-40 grayscale transition" onclick="openActivityDetail('my_activity')">
  199. <div class="flex items-center gap-4 w-full pr-2">
  200. <img src="https://images.unsplash.com/photo-1563986768494-4dee2763ff3f?w=100&h=100&fit=crop" class="w-14 h-14 object-cover shrink-0">
  201. <div class="flex-1 overflow-hidden">
  202. <h4 class="text-[14px] text-gray-900 font-medium truncate mb-1">2026 汇丰中国理财讲座</h4>
  203. <div class="text-[12px] text-gray-400">2026年/9月20日 周六14:00</div>
  204. </div>
  205. </div>
  206. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5"><path d="m9 18 6-6-6-6"/></svg>
  207. </div>
  208. <div class="flex items-center justify-between py-4 px-5 border-b border-gray-100 cursor-pointer opacity-40 grayscale transition" onclick="openActivityDetail('my_activity')">
  209. <div class="flex items-center gap-4 w-full pr-2">
  210. <img src="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=100&h=100&fit=crop" class="w-14 h-14 object-cover shrink-0">
  211. <div class="flex-1 overflow-hidden">
  212. <h4 class="text-[14px] text-gray-900 font-medium truncate mb-1">汇丰中国 3Q 层层升级礼遇</h4>
  213. <div class="text-[12px] text-gray-400">2026年/12月12日-2026年/12月24日</div>
  214. </div>
  215. </div>
  216. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5"><path d="m9 18 6-6-6-6"/></svg>
  217. </div>
  218. <div class="px-5 py-4 text-[12px] text-gray-500 leading-relaxed">
  219. 温馨提示:如需查看信用卡活动进度,请点击进行中-信用卡活动专区。
  220. </div>
  221. </div>
  222. </div>
  223. </div>
  224. <div id="all-activities-view" class="absolute inset-0 bg-white flex flex-col view-transition translate-x-full z-40">
  225. <header class="h-11 flex items-center justify-between px-4 bg-white z-20 shrink-0">
  226. <button onclick="closeAllActivities()" class="text-gray-800 p-1"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg></button>
  227. <h1 class="text-[17px] font-medium text-gray-900 absolute left-1/2 -translate-x-1/2">全部活动</h1>
  228. <div class="w-8"></div>
  229. </header>
  230. <!-- All Activities Tabs -->
  231. <div class="flex border-b border-gray-200 shrink-0 relative">
  232. <button class="flex-1 py-3 text-[14px] font-medium text-gray-900 text-center" onclick="switchAllActivitiesTab('online')" id="tab-all-online">线上活动</button>
  233. <button class="flex-1 py-3 text-[14px] font-medium text-gray-500 text-center" onclick="switchAllActivitiesTab('offline')" id="tab-all-offline">线下活动</button>
  234. <div id="all-activities-indicator" class="absolute bottom-0 h-[2px] bg-[#d70014] transition-all duration-300" style="width: 50%; left: 0%;"></div>
  235. </div>
  236. <div class="flex-1 overflow-y-auto no-scrollbar bg-white relative">
  237. <!-- 数据容器 -->
  238. <div id="all-activities-content" class="pb-24 flex flex-col min-h-full">
  239. <!-- JS Render Here -->
  240. </div>
  241. <!-- 信用卡入口 (固定在内容区底部) -->
  242. <div class="absolute bottom-6 left-5 right-5 h-12 border border-[#d70014] rounded text-[#d70014] flex items-center justify-center font-medium text-[15px] cursor-pointer active:bg-gray-50 transition bg-white" onclick="showToast('正在跳转信用卡平台...')">
  243. 信用卡活动专区入口
  244. </div>
  245. </div>
  246. </div>
  247. <div id="activity-detail-view" class="absolute inset-0 bg-white flex flex-col view-transition translate-x-full z-45">
  248. <header class="h-11 flex items-center justify-between px-4 bg-white/90 backdrop-blur-sm z-20 shrink-0 absolute top-0 left-0 w-full">
  249. <button onclick="closeActivityDetail()" class="text-gray-800 p-1"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg></button>
  250. <h1 class="text-[17px] font-medium text-gray-900 absolute left-1/2 -translate-x-1/2">活动详情</h1>
  251. <div class="w-8"></div>
  252. </header>
  253. <div class="flex-1 overflow-y-auto no-scrollbar pb-24 relative bg-white">
  254. <img src="https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=800&auto=format&fit=crop" class="w-full h-[220px] object-cover shrink-0 block">
  255. <div class="px-5 py-5">
  256. <h2 class="text-[20px] font-medium text-gray-900 mb-4 tracking-wide">2025 汇丰母亲节特别活动</h2>
  257. <div class="text-[13px] text-gray-600 space-y-2 mb-8 tracking-wide">
  258. <p>活动对象:文字文字文字</p>
  259. <p>活动报名时间:年/月/日/时-年/月/日/时</p>
  260. <p>活动进行时间:年/月/日/时-年/月/日/时</p>
  261. <p>活动地点:北京华贸丽思卡尔顿酒店大宴会厅</p>
  262. <p>报名人数:数字(配置则展示)</p>
  263. </div>
  264. <h3 class="text-[17px] font-medium text-gray-900 mb-4">活动详情</h3>
  265. <div class="text-[14px] text-gray-600 leading-[1.8] mb-8 text-justify tracking-wide">
  266. <p class="mb-4">带领嘉宾初步进入风水的世界,探索古人智慧的深度。用传统智慧理念和专业的财富规划帮助客户更好把握机遇。实现财富积累,代代传承。</p>
  267. <p class="mb-4">特邀嘉宾:赵丹青<br>现系国际青年易经学会会员;<br>香港中美易经学会会员及“中华周易学会”理事;<br>担任香港《楼盘与风水》杂志编委,《财务圈》杂志上开设风水专栏,为LV, Tiffany, Ports等著名国际时尚品牌公司堪舆;<br>任澳大利亚周易研究会副会长。</p>
  268. <p>活动流程:<br>14:00 - 14:30 嘉宾签到<br>14:30 - 14:45 汇丰私人财富规划简介<br>14:45 - 16:15 嘉宾主题分享-习住家风水 创财富人生<br>16:15 - 17:00 茶歇&自由交流</p>
  269. </div>
  270. <!-- 层级 Tabs -->
  271. <div class="flex border-b border-gray-200 mb-5 relative">
  272. <button class="flex-1 py-3 text-[14px] font-medium text-gray-900 text-center" onclick="switchDetailTier(1)" id="tier-tab-1">层级一</button>
  273. <button class="flex-1 py-3 text-[14px] font-medium text-gray-500 text-center" onclick="switchDetailTier(2)" id="tier-tab-2">层级二</button>
  274. <button class="flex-1 py-3 text-[14px] font-medium text-gray-500 text-center" onclick="switchDetailTier(3)" id="tier-tab-3">层级三</button>
  275. <button class="flex-1 py-3 text-[14px] font-medium text-gray-500 text-center" onclick="switchDetailTier(4)" id="tier-tab-4">层级四</button>
  276. <div id="detail-tier-indicator" class="absolute bottom-0 h-[2px] bg-[#d70014] transition-all duration-300" style="width: 25%; left: 0%;"></div>
  277. </div>
  278. <!-- 层级内容 -->
  279. <div>
  280. <h4 class="text-[15px] font-medium text-gray-900 mb-2">达标要求</h4>
  281. <p class="text-[14px] text-gray-600 leading-[1.6] mb-5 tracking-wide">活动期间,首次升级为“合格卓越理财”客户,且保持3个月,3个自然月任一月末日终,较2025年3月末新增持有1类以上资产大类。</p>
  282. <h4 class="text-[15px] font-medium text-gray-900 mb-2">达标奖励</h4>
  283. <p class="text-[14px] text-gray-600 leading-[1.6] tracking-wide mb-10">32万汇友荟积分(可兑换约等值¥500好礼)</p>
  284. </div>
  285. </div>
  286. </div>
  287. <!-- 底部操作栏 -->
  288. <div class="absolute bottom-0 left-0 w-full h-[68px] bg-white border-t border-gray-100 flex items-center justify-center px-4 z-30 shrink-0 shadow-[0_-2px_10px_rgba(0,0,0,0.03)] pb-safe">
  289. <button id="detail-main-btn" class="w-full h-11 bg-[#d70014] text-white text-[16px] font-medium rounded-[2px]" onclick="handleDetailAction()">报名</button>
  290. </div>
  291. </div>
  292. </div> <!-- 结束视图层容器 -->
  293. <!-- 积分规则弹窗 -->
  294. <div id="rule-modal-backdrop" class="absolute inset-0 bg-black/50 z-[60] opacity-0 pointer-events-none transition-opacity duration-300" onclick="closeRuleModal()"></div>
  295. <div id="rule-modal" class="absolute bottom-0 left-0 w-full bg-white z-[70] rounded-t-2xl transform translate-y-full transition-transform duration-300 flex flex-col" style="height: 85%;">
  296. <div class="flex justify-between items-center px-5 py-4 border-b border-gray-100 shrink-0">
  297. <h2 class="text-[17px] font-medium text-gray-900">积分规则</h2>
  298. <button onclick="closeRuleModal()" class="text-gray-500 p-1 hover:bg-gray-100 rounded-full transition">
  299. <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
  300. </button>
  301. </div>
  302. <div class="flex-1 overflow-y-auto px-5 py-5 no-scrollbar">
  303. <h3 class="text-[14px] font-medium text-gray-800 mb-4">汇丰银行(中国)有限公司积分使用规则</h3>
  304. <div class="text-[13px] text-gray-600 leading-[1.8] space-y-4 text-justify">
  305. <p>本积分使用规则(“本规则”)为汇丰银行(中国)有限公司(“本行”)的客户(“客户”)使用本行信用卡及借记卡积分(即“汇友荟”汇丰客户关系积分)(简称“汇友荟积分”)兑换本行提供的礼品、里程或刷卡金等项目(具体以本行积分换礼平台正式公布的有效兑换项目为准,以下称“兑换项目”)等需要遵守的使用规则。</p>
  306. <p class="font-medium text-gray-800 pt-2">一、 兑换及使用渠道</p>
  307. <p>1. 除本规则以下相关兑换项目项下有特别说明外,客户可使用信用卡积分及“汇友荟”汇丰客户关系积分在积分换礼平台兑换相关项目,具体兑换渠道如下:<br>
  308. (1) 登录“汇丰中国客户服务”微信服务号,点击“我的汇丰”—“我的积分”—“积分商城”页面兑换;或<br>
  309. (2) 登录汇丰银行App,在“我的”点击“我的积分”—“积分商城”页面兑换礼品;<br>
  310. (3) 人工服务申请兑换:拨打本行24小时客服热线95366或4008695366(该渠道仅限本行信用卡主卡持卡人兑换部分项目申请时使用)兑换。</p>
  311. <p>2. 积分一旦兑换成功,不可撤销、更改或退还。若因系统或网络原因导致兑换失败,积分将自动退回账户。</p>
  312. <p class="font-medium text-gray-800 pt-2">二、 积分有效期</p>
  313. <p>信用卡积分有效期与信用卡有效期一致。如客户在信用卡有效期届满前未消耗完毕,积分将于信用卡到期次月清零。“汇友荟”积分有效期详见对应活动的具体规则。请客户关注积分到期日并及时兑换。</p>
  314. <p class="pt-10 pb-10 text-center text-gray-300">-- 到底了 --</p>
  315. </div>
  316. </div>
  317. </div>
  318. <!-- 余额计算规则弹窗 -->
  319. <div id="balance-rule-modal-backdrop" class="absolute inset-0 bg-black/50 z-[60] opacity-0 pointer-events-none transition-opacity duration-300" onclick="closeBalanceRuleModal()"></div>
  320. <div id="balance-rule-modal" class="absolute bottom-0 left-0 w-full bg-white z-[70] rounded-t-2xl transform translate-y-full transition-transform duration-300 flex flex-col min-h-[220px]">
  321. <div class="flex justify-between items-center px-5 py-4 shrink-0 border-b border-gray-50">
  322. <h2 class="text-[18px] font-medium text-gray-900">提示</h2>
  323. <button onclick="closeBalanceRuleModal()" class="text-gray-500 p-1 hover:bg-gray-100 rounded-full transition">
  324. <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
  325. </button>
  326. </div>
  327. <div class="flex-1 px-5 pb-8 pt-4">
  328. <div class="text-[15px] text-gray-700 leading-[1.6]">
  329. 月内日均余额 = 当月内每日账户余额之和 / 当月天数。
  330. </div>
  331. </div>
  332. </div>
  333. <!-- 报名通用提示弹窗 (不符合/已满/互斥) -->
  334. <div id="alert-modal-backdrop" class="absolute inset-0 bg-black/50 z-[60] opacity-0 pointer-events-none transition-opacity duration-300" onclick="closeAlertModal()"></div>
  335. <div id="alert-modal" class="absolute bottom-0 left-0 w-full bg-white z-[70] rounded-t-xl transform translate-y-full transition-transform duration-300 flex flex-col min-h-[200px]">
  336. <div class="flex justify-between items-center px-5 py-4 shrink-0">
  337. <h2 class="text-[17px] font-medium text-gray-900">提示</h2>
  338. <button onclick="closeAlertModal()" class="text-gray-500 p-1 hover:bg-gray-100 rounded-full transition">
  339. <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
  340. </button>
  341. </div>
  342. <div class="px-5 pb-6 pt-2 text-[15px] text-gray-700" id="alert-modal-msg">
  343. 您暂不符合报名条件
  344. </div>
  345. <div class="px-5 pb-8">
  346. <button onclick="closeAlertModal()" class="w-full py-2.5 border border-[#cccccc] rounded-[2px] text-[16px] text-gray-800 bg-white font-medium active:bg-gray-50">返回首页</button>
  347. </div>
  348. </div>
  349. <!-- 报名确认弹窗 -->
  350. <div id="confirm-modal-backdrop" class="absolute inset-0 bg-black/50 z-[60] opacity-0 pointer-events-none transition-opacity duration-300" onclick="closeConfirmModal()"></div>
  351. <div id="confirm-modal" class="absolute bottom-0 left-0 w-full bg-white z-[70] rounded-t-xl transform translate-y-full transition-transform duration-300 flex flex-col min-h-[220px]">
  352. <div class="flex justify-between items-center px-5 py-4 shrink-0">
  353. <h2 class="text-[17px] font-medium text-gray-900">报名确认</h2>
  354. <button onclick="closeConfirmModal()" class="text-gray-500 p-1 hover:bg-gray-100 rounded-full transition">
  355. <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
  356. </button>
  357. </div>
  358. <div class="px-5 pb-8 pt-2 text-[15px] text-gray-700">
  359. 你确定报名参加此「2025 汇丰母亲节特别活动」?
  360. </div>
  361. <div class="flex px-5 pb-8 gap-3">
  362. <button onclick="closeConfirmModal()" class="flex-1 py-2.5 border border-[#cccccc] rounded-[2px] text-[16px] text-gray-800 bg-white font-medium active:bg-gray-50">取消</button>
  363. <button onclick="closeConfirmModal(); showToast('报名成功')" class="flex-1 py-2.5 rounded-[2px] text-[16px] text-white bg-[#d70014] font-medium active:opacity-90">确认</button>
  364. </div>
  365. </div>
  366. <!-- Toast -->
  367. <div id="toast" class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-black/80 text-white px-4 py-2 rounded-md text-[14px] z-[100] opacity-0 pointer-events-none transition-opacity duration-300 text-center w-max max-w-[80%]"></div>
  368. <!-- 底部 Home Indicator (iOS风格) -->
  369. <div class="h-8 w-full flex justify-center items-center z-[80] absolute bottom-0 pointer-events-none">
  370. <div class="w-32 h-1 bg-gray-900 rounded-full"></div>
  371. </div>
  372. </div>
  373. <script>
  374. // --- 图标库 ---
  375. const icons = {
  376. cap: `<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M22 10v6M2 10l10-5 10 5-10 5z"/><path d="M6 12v5c3 3 9 3 12 0v-5"/></svg>`,
  377. heart: `<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/></svg>`,
  378. bulb: `<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.9 1.2 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg>`,
  379. gift: `<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><rect x="3" y="8" width="18" height="14" rx="2"/><path d="M12 5a3 3 0 1 0-3 3"/><path d="M15 8a3 3 0 1 0-3-3"/><path d="M12 8v14"/><path d="M3 12h18"/></svg>`,
  380. diamond: `<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M6 3h12l4 6-10 13L2 9Z"/></svg>`,
  381. medbox: `<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><rect x="4" y="8" width="16" height="12" rx="2"/><path d="M8 8V6a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M12 11v4M10 13h4"/></svg>`,
  382. car: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M19 17h2c.6 0 1-.4 1-1v-3c0-.9-.7-1.7-1.5-1.9C18.7 10.6 16 10 12 10s-6.7.6-8.5 1.1C2.7 11.3 2 12.1 2 13v3c0 .6.4 1 1 1h2"/><rect width="16" height="8" x="4" y="10" rx="2"/><path d="M7 14h.01"/><path d="M17 14h.01"/><path d="m10 10-1.5-4h7l-1.5 4"/></svg>`,
  383. plane: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.2-1.1.7l-1.2 3.6 7.7 4.4-4.2 4.2-3.1-.8c-.4-.1-.8.1-1 .5l-.8 2.3 5.4 1.8 1.8 5.4 2.3-.8c.4-.2.6-.6.5-1l-.8-3.1 4.2-4.2 4.4 7.7 3.6-1.2c.5-.2.8-.6.7-1.1Z"/></svg>`,
  384. globe: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"><circle cx="12" cy="12" r="10"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/><path d="M2 12h20"/></svg>`,
  385. coffee: `<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#ba6534" stroke-width="1.5"><path d="M17 8h1a4 4 0 1 1 0 8h-1"/><path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4Z"/><line x1="6" x2="6" y1="2" y2="4"/><line x1="10" x2="10" y1="2" y2="4"/><line x1="14" x2="14" y1="2" y2="4"/></svg>`,
  386. planeRed: `<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#d70014" stroke-width="1.5"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.2-1.1.7l-1.2 3.6 7.7 4.4-4.2 4.2-3.1-.8c-.4-.1-.8.1-1 .5l-.8 2.3 5.4 1.8 1.8 5.4 2.3-.8c.4-.2.6-.6.5-1l-.8-3.1 4.2-4.2 4.4 7.7 3.6-1.2c.5-.2.8-.6.7-1.1Z"/></svg>`,
  387. help: `<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5" class="ml-1 inline-block"><circle cx="12" cy="12" r="10"></circle><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>`,
  388. copy: `<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5" class="ml-1 inline-block cursor-pointer"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`,
  389. doc: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="mr-1 inline-block"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>`,
  390. clipboard: `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>`,
  391. clock: `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="inline-block mr-1"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`,
  392. chevronRight: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="1.5"><path d="m9 18 6-6-6-6"/></svg>`
  393. };
  394. // --- 全局状态 ---
  395. let currentUserLevel = 'advance';
  396. let currentHomeTab = 'premier';
  397. let currentAllActivitiesTab = 'online';
  398. let hasAllActivitiesData = true;
  399. let currentDetailBtnState = 'enroll';
  400. let currentEnrollCondition = 'success';
  401. let isSimPanelOpen = false;
  402. let isDataPanelOpen = false;
  403. let isDetailBtnPanelOpen = false;
  404. let isEnrollResultPanelOpen = false;
  405. // --- 初始化渲染 ---
  406. document.addEventListener('DOMContentLoaded', () => {
  407. // 修复:直接使用 setUserLevel 完成页面数据渲染和侧边栏状态的高亮初始化
  408. setUserLevel('advance');
  409. });
  410. // --- ====== View A: 首页逻辑 ====== ---
  411. const homeTabData = {
  412. premier: { title: '卓越理财', subtitle: '为您和您的家人提供尊崇专属礼遇和权益', imageSrc: 'https://images.unsplash.com/photo-1518893494013-481c1d8ed3fd?w=800&fit=crop', features: [{ title: '国际教育', desc: '享专属国际化学校优惠及背景提升项目' }, { title: '酒店礼遇', desc: '高端酒店及商场会籍匹配' }, { title: '环球礼遇', desc: '汇丰中国信用卡持卡人,尊享机场贵宾厅及礼宾车等礼遇' }] },
  413. elite: { title: '卓越理财·尊尚', subtitle: '开启尊尚体验,享受环球出行、高端运动等VIP礼遇。', imageSrc: 'https://images.unsplash.com/photo-1610701596007-11502861dcfa?w=800&fit=crop', features: [{ title: '国际教育', desc: '享多项升学规划指导' }, { title: '专属礼宾服务', desc: '高端酒店及商场会籍匹配' }, { title: '尊尚生日礼', desc: '尊享体验任选其一' }] },
  414. global: { title: '环球私人银行', subtitle: '开启环球「创臻享」及「汇佑健康」之旅。', imageSrc: 'https://images.unsplash.com/photo-1550684848-fac1c5b4e853?w=800&fit=crop', features: [{ title: '高尔夫权益', desc: '练习场畅达及VIP预约' }, { title: '臻享体验', desc: '米其林级别私宴预定服务等' }, { title: '环球旅行', desc: '境外接送机及升房礼遇' }] }
  415. };
  416. function renderHomePage() {
  417. const isBasic = ['prospect', 'credit_only', 'advance'].includes(currentUserLevel);
  418. document.getElementById('no-tier-banner').style.display = isBasic ? 'flex' : 'none';
  419. ['premier', 'elite', 'global'].forEach((tab, index) => {
  420. const btn = document.getElementById(`tab-btn-${tab}`);
  421. btn.className = (tab === currentHomeTab) ? "flex-1 py-3 text-[14px] font-medium relative text-gray-900" : "flex-1 py-3 text-[14px] font-medium relative text-gray-500";
  422. if(tab === currentHomeTab) document.getElementById('tab-indicator').style.left = `${index * 33.33}%`;
  423. });
  424. const data = homeTabData[currentHomeTab];
  425. const levelWeights = { 'prospect': 0, 'credit_only': 0, 'advance': 0, 'premier': 1, 'elite': 2, 'global': 3 };
  426. const isUnlocked = levelWeights[currentUserLevel] >= levelWeights[currentHomeTab];
  427. let buttonHTML = isUnlocked ? `<button onclick="showZone()" class="w-full py-3 mt-4 mb-6 border border-gray-300 rounded text-[15px] font-medium text-gray-900 bg-white active:bg-gray-50 transition">进入权益专区</button>` : `<button class="w-full py-3 mt-4 mb-6 rounded text-[15px] font-medium text-[#b3b3b3] bg-[#f7f7f7] cursor-not-allowed">待解锁</button>`;
  428. let featuresHTML = currentHomeTab === 'global'
  429. ? '<div class="flex flex-col border-t border-gray-100 pt-2">' + data.features.map(f => `<div class="flex items-center gap-4 py-4 border-b border-gray-100 last:border-0"><div class="w-12 h-12 rounded-full border border-gray-200 bg-[#fbfcfc] flex items-center justify-center text-gray-500 shrink-0">${icons.clipboard}</div><div class="flex-1"><div class="text-[15px] font-medium text-gray-900">${f.title}</div><div class="text-[13px] text-gray-500 mt-1">${f.desc}</div></div></div>`).join('') + '</div>'
  430. : '<ul class="text-sm">' + data.features.map(f => `<li class="flex items-start mb-3 relative pl-3"><span class="absolute left-0 top-[8px] w-1 h-1 bg-gray-600 rounded-full"></span><div><span class="text-gray-900 font-medium text-[13px] mr-1">${f.title}</span><p class="text-gray-500 text-[12px] leading-relaxed mt-0.5">${f.desc}</p></div></li>`).join('') + '</ul>';
  431. document.getElementById('content-area').innerHTML = `
  432. <div class="w-full h-[150px] overflow-hidden mb-4 rounded-sm"><img src="${data.imageSrc}" class="w-full h-full object-cover"></div>
  433. <h2 class="text-xl font-medium text-gray-900 mb-1.5">${data.title}</h2>
  434. <p class="text-[13px] text-gray-500 leading-snug">${data.subtitle}</p>
  435. ${buttonHTML}
  436. ${featuresHTML}
  437. `;
  438. }
  439. function switchTab(tabId) { currentHomeTab = tabId; renderHomePage(); }
  440. // --- ====== View B: 专区页逻辑 (包含精彩活动) ====== ---
  441. function renderZonePage() {
  442. // (简化的层级数据,确保框架完整)
  443. const isAdvanced = ['premier', 'elite', 'global'].includes(currentUserLevel);
  444. const isPointsExpanded = isAdvanced;
  445. let html = `<div class="w-full h-2 bg-transparent shrink-0"></div>`; // Placeholder top
  446. // 积分与常用兑换 (省略具体判断,展示全貌)
  447. html += `
  448. <div class="bg-white px-5 py-5 pb-8 mb-2 shadow-sm border-b border-gray-100">
  449. <div class="flex justify-between items-center mb-5">
  450. <h3 class="text-[16px] font-medium text-gray-900">积分商城</h3>
  451. <div class="text-[13px] text-gray-900 font-medium flex items-center">进入积分商城 <svg class="w-4 h-4 ml-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 18l6-6-6-6"/></svg></div>
  452. </div>
  453. <div class="flex justify-between items-center py-2 border-b border-transparent">
  454. <div><div class="text-[20px] font-medium text-gray-800">3,000,000</div><div class="text-[12px] text-gray-400 mt-1">我的总积分</div></div>
  455. </div>
  456. <div class="mt-6">
  457. <div class="flex justify-between items-center mb-4">
  458. <h3 class="text-[16px] font-medium text-gray-900">常用兑换</h3>
  459. <div class="text-[13px] text-gray-800 flex items-center font-medium">${icons.doc}积分明细</div>
  460. </div>
  461. <div class="flex overflow-x-auto no-scrollbar gap-3 pb-2 snap-x">
  462. <div class="min-w-[100px] bg-white border border-gray-200 rounded-sm flex flex-col items-center snap-start shadow-sm"><div class="w-full h-[65px] bg-[#f7fbff] flex items-center justify-center">${icons.planeRed}</div><div class="text-[13px] text-gray-700 py-2.5">航空里程</div></div>
  463. <div class="min-w-[100px] bg-white border border-gray-200 rounded-sm flex flex-col items-center snap-start shadow-sm"><div class="w-full h-[65px] bg-[#fffaf5] flex items-center justify-center">${icons.coffee}</div><div class="text-[13px] text-gray-700 py-2.5">贵宾厅</div></div>
  464. <div class="min-w-[100px] bg-white border border-gray-200 rounded-sm flex flex-col items-center snap-start shadow-sm"><div class="w-full h-[65px] bg-[#f9fafb] flex items-center justify-center"><svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#666" stroke-width="1.5"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/></svg></div><div class="text-[13px] text-gray-700 py-2.5">年费</div></div>
  465. </div>
  466. <div class="flex justify-end mt-6">
  467. <div class="text-[12px] text-gray-900 font-medium flex items-center cursor-pointer" onclick="openRuleModal()">${icons.doc} 积分规则</div>
  468. </div>
  469. </div>
  470. </div>
  471. `;
  472. // 【新增】精彩活动模块 (6个等级都有)
  473. html += `
  474. <div class="bg-white pb-8 shadow-sm">
  475. <div class="px-5 py-5 flex justify-between items-center">
  476. <h3 class="text-[18px] font-medium text-gray-900">精彩活动</h3>
  477. <div class="text-[13px] text-gray-900 font-medium flex items-center cursor-pointer" onclick="showMyActivities()">我的活动 ${icons.chevronRight}</div>
  478. </div>
  479. <!-- 轮播大图卡片 -->
  480. <div class="px-5 mb-8">
  481. <div class="w-full bg-white flex flex-col cursor-pointer active:opacity-90 transition shadow-sm border border-gray-100 rounded-b-sm" onclick="openActivityDetail('normal')">
  482. <img src="https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=800&auto=format&fit=crop" class="w-full h-[160px] object-cover">
  483. <div class="p-4">
  484. <h4 class="text-[17px] text-gray-900 font-bold mb-2 truncate">汇丰层层升级礼</h4>
  485. <div class="text-[13px] text-gray-500 mb-1.5">线上活动</div>
  486. <div class="text-[12px] text-gray-400 flex items-center">${icons.clock} 2026/1/1 8:00 - 2026/3/31 23:29</div>
  487. </div>
  488. </div>
  489. <div class="flex justify-center gap-2 mt-4">
  490. <div class="w-[4px] h-[4px] bg-black rounded-full border border-black"></div>
  491. <div class="w-[4px] h-[4px] border border-black rounded-full"></div>
  492. </div>
  493. </div>
  494. <!-- 活动列表 -->
  495. <div class="px-5">
  496. <div class="flex items-center justify-between py-4 border-b border-gray-100 cursor-pointer active:bg-gray-50" onclick="openActivityDetail('normal')">
  497. <div class="flex items-center gap-4 w-full pr-2">
  498. <img src="https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=100&h=100&fit=crop" class="w-[60px] h-[60px] object-cover shrink-0 rounded-[2px]">
  499. <div class="flex-1 overflow-hidden">
  500. <h4 class="text-[15px] text-gray-900 font-medium truncate mb-1">汇丰卓越理财新户达标礼</h4>
  501. <div class="text-[13px] text-gray-500 mb-0.5">线上活动</div>
  502. <div class="text-[12px] text-gray-400">2026/1/1 8:00 - 2026/3/31 23:29</div>
  503. </div>
  504. </div>
  505. ${icons.chevronRight}
  506. </div>
  507. <div class="flex items-center justify-between py-4 border-b border-gray-100 cursor-pointer active:bg-gray-50" onclick="openActivityDetail('normal')">
  508. <div class="flex items-center gap-4 w-full pr-2">
  509. <img src="https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?w=100&h=100&fit=crop" class="w-[60px] h-[60px] object-cover shrink-0 rounded-[2px]">
  510. <div class="flex-1 overflow-hidden">
  511. <h4 class="text-[15px] text-gray-900 font-medium truncate mb-1">汇丰 1+1 新客户礼遇</h4>
  512. <div class="text-[13px] text-gray-500 mb-0.5">线上活动</div>
  513. <div class="text-[12px] text-gray-400">2026/1/1 8:00 - 2026/3/31 23:29</div>
  514. </div>
  515. </div>
  516. ${icons.chevronRight}
  517. </div>
  518. <div class="flex items-center justify-between py-4 border-b border-gray-100 cursor-pointer active:bg-gray-50" onclick="openActivityDetail('normal')">
  519. <div class="flex items-center gap-4 w-full pr-2">
  520. <img src="https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=100&h=100&fit=crop" class="w-[60px] h-[60px] object-cover shrink-0 rounded-[2px]">
  521. <div class="flex-1 overflow-hidden">
  522. <h4 class="text-[15px] text-gray-900 font-medium truncate mb-1">汇丰财富峰会</h4>
  523. <div class="text-[13px] text-gray-500 mb-0.5">线下活动</div>
  524. <div class="text-[12px] text-gray-400">2026/1/1 8:00 - 2026/3/31 23:29</div>
  525. </div>
  526. </div>
  527. ${icons.chevronRight}
  528. </div>
  529. <button onclick="showAllActivities()" class="w-full py-3 mt-6 bg-[#f7f7f7] text-[#333] font-medium border border-[#ddd] rounded-sm text-[14px] active:bg-gray-200 transition">
  530. 更多活动
  531. </button>
  532. </div>
  533. </div>
  534. `;
  535. document.getElementById('zone-content-area').innerHTML = html;
  536. }
  537. // --- ====== View Navigation ====== ---
  538. function showZone() {
  539. renderZonePage();
  540. document.getElementById('home-view').classList.add('-translate-x-full');
  541. document.getElementById('zone-view').classList.remove('translate-x-full');
  542. }
  543. function hideZone() {
  544. document.getElementById('home-view').classList.remove('-translate-x-full');
  545. document.getElementById('zone-view').classList.add('translate-x-full');
  546. }
  547. function showMyActivities() { document.getElementById('my-activities-view').classList.remove('translate-x-full'); }
  548. function closeMyActivities() { document.getElementById('my-activities-view').classList.add('translate-x-full'); }
  549. function showAllActivities() {
  550. renderAllActivitiesList();
  551. document.getElementById('all-activities-view').classList.remove('translate-x-full');
  552. document.getElementById('data-control-wrapper').classList.remove('hidden');
  553. }
  554. function closeAllActivities() {
  555. document.getElementById('all-activities-view').classList.add('translate-x-full');
  556. document.getElementById('data-control-wrapper').classList.add('hidden');
  557. if(isDataPanelOpen) toggleDataPanel();
  558. }
  559. function openActivityDetail(source) {
  560. document.getElementById('activity-detail-view').classList.remove('translate-x-full');
  561. // 根据入口设置按钮
  562. if (source === 'my_activity') setDetailBtnState('progress');
  563. else setDetailBtnState('enroll');
  564. // 面板切换
  565. document.getElementById('data-control-wrapper').classList.add('hidden');
  566. document.getElementById('detail-control-wrapper').classList.remove('hidden');
  567. document.getElementById('detail-control-wrapper').classList.add('flex');
  568. }
  569. function closeActivityDetail() {
  570. document.getElementById('activity-detail-view').classList.add('translate-x-full');
  571. document.getElementById('detail-control-wrapper').classList.add('hidden');
  572. document.getElementById('detail-control-wrapper').classList.remove('flex');
  573. if(isDetailBtnPanelOpen) toggleDetailBtnPanel();
  574. if(isEnrollResultPanelOpen) toggleEnrollResultPanel();
  575. if(!document.getElementById('all-activities-view').classList.contains('translate-x-full')) {
  576. document.getElementById('data-control-wrapper').classList.remove('hidden');
  577. }
  578. }
  579. // --- ====== Tabs Logic ====== ---
  580. function switchActivitiesTab(tabId) {
  581. const tabs = ['ongoing', 'ended'];
  582. tabs.forEach(t => {
  583. document.getElementById(`tab-my-${t}`).className = t === tabId ? 'flex-1 py-3 text-[15px] font-medium text-gray-900 text-center' : 'flex-1 py-3 text-[15px] font-medium text-gray-500 text-center';
  584. const content = document.getElementById(`activities-${t}-content`);
  585. if(t === tabId) content.classList.remove('hidden'); else content.classList.add('hidden');
  586. });
  587. document.getElementById('my-activities-indicator').style.left = tabId === 'ongoing' ? '0%' : '50%';
  588. }
  589. function switchAllActivitiesTab(tab) {
  590. currentAllActivitiesTab = tab;
  591. document.getElementById('tab-all-online').className = tab === 'online' ? 'flex-1 py-3 text-[14px] font-medium text-gray-900 text-center' : 'flex-1 py-3 text-[14px] font-medium text-gray-500 text-center';
  592. document.getElementById('tab-all-offline').className = tab === 'offline' ? 'flex-1 py-3 text-[14px] font-medium text-gray-900 text-center' : 'flex-1 py-3 text-[14px] font-medium text-gray-500 text-center';
  593. document.getElementById('all-activities-indicator').style.left = tab === 'online' ? '0%' : '50%';
  594. renderAllActivitiesList();
  595. }
  596. function switchDetailTier(tier) {
  597. document.getElementById('detail-tier-indicator').style.left = `${(tier - 1) * 25}%`;
  598. for (let i = 1; i <= 4; i++) {
  599. document.getElementById(`tier-tab-${i}`).className = i === tier ? 'flex-1 py-3 text-[14px] font-medium text-gray-900 text-center' : 'flex-1 py-3 text-[14px] font-medium text-gray-500 text-center';
  600. }
  601. }
  602. // --- ====== Data Logic ====== ---
  603. function setAllActivitiesData(hasData) {
  604. hasAllActivitiesData = hasData;
  605. document.getElementById('btn-data-true').className = hasData ? 'px-2 py-1.5 rounded bg-[#d70014] text-white transition font-medium' : 'px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition';
  606. document.getElementById('btn-data-false').className = !hasData ? 'px-2 py-1.5 rounded bg-[#d70014] text-white transition font-medium' : 'px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition';
  607. renderAllActivitiesList();
  608. toggleDataPanel();
  609. }
  610. function renderAllActivitiesList() {
  611. const container = document.getElementById('all-activities-content');
  612. if (!hasAllActivitiesData) {
  613. container.innerHTML = `<div class="flex flex-col items-center justify-center pt-24 text-gray-400"><svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" class="mb-4"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="9" x2="15" y2="15"></line><line x1="15" y1="9" x2="9" y2="15"></line></svg><p class="text-[14px]">暂无可参与活动</p></div>`;
  614. return;
  615. }
  616. const list = currentAllActivitiesTab === 'online' ? [1,2,3,4] : [1,2];
  617. const suffix = currentAllActivitiesTab === 'online' ? '线上' : '线下';
  618. let html = list.map(i => `
  619. <div class="flex items-center justify-between py-4 px-5 border-b border-gray-100 cursor-pointer active:bg-gray-50 transition" onclick="openActivityDetail('normal')">
  620. <div class="flex items-center gap-4 w-full pr-2">
  621. <img src="https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=100&h=100&fit=crop" class="w-[60px] h-[60px] object-cover shrink-0 rounded-[2px]">
  622. <div class="flex-1 overflow-hidden">
  623. <h4 class="text-[15px] text-gray-900 font-medium truncate mb-1">汇丰${suffix}活动示例 ${i}</h4>
  624. <div class="text-[12px] text-gray-400">2026/09/20 周六 14:00</div>
  625. </div>
  626. </div>
  627. ${icons.chevronRight}
  628. </div>
  629. `).join('');
  630. container.innerHTML = html;
  631. }
  632. // --- ====== Panels & Modals ====== ---
  633. function toggleSimPanel() {
  634. isSimPanelOpen = !isSimPanelOpen;
  635. const panel = document.getElementById('sim-panel');
  636. isSimPanelOpen ? panel.classList.remove('scale-0', 'opacity-0', 'pointer-events-none') : panel.classList.add('scale-0', 'opacity-0', 'pointer-events-none');
  637. }
  638. function toggleDataPanel() {
  639. isDataPanelOpen = !isDataPanelOpen;
  640. const panel = document.getElementById('data-panel');
  641. isDataPanelOpen ? panel.classList.remove('scale-0', 'opacity-0', 'pointer-events-none') : panel.classList.add('scale-0', 'opacity-0', 'pointer-events-none');
  642. }
  643. function toggleDetailBtnPanel() {
  644. isDetailBtnPanelOpen = !isDetailBtnPanelOpen;
  645. const panel = document.getElementById('detail-btn-panel');
  646. if (isDetailBtnPanelOpen) { panel.classList.remove('scale-0', 'opacity-0', 'pointer-events-none'); if(isEnrollResultPanelOpen) toggleEnrollResultPanel(); }
  647. else panel.classList.add('scale-0', 'opacity-0', 'pointer-events-none');
  648. }
  649. function toggleEnrollResultPanel() {
  650. isEnrollResultPanelOpen = !isEnrollResultPanelOpen;
  651. const panel = document.getElementById('enroll-result-panel');
  652. if (isEnrollResultPanelOpen) { panel.classList.remove('scale-0', 'opacity-0', 'pointer-events-none'); if(isDetailBtnPanelOpen) toggleDetailBtnPanel(); }
  653. else panel.classList.add('scale-0', 'opacity-0', 'pointer-events-none');
  654. }
  655. function setUserLevel(level) {
  656. currentUserLevel = level;
  657. currentHomeTab = ['prospect', 'credit_only', 'advance'].includes(level) ? 'premier' : level;
  658. renderHomePage();
  659. if(!document.getElementById('zone-view').classList.contains('translate-x-full')) renderZonePage();
  660. ['prospect', 'credit_only', 'advance', 'premier', 'elite', 'global'].forEach(lvl => {
  661. document.getElementById(`btn-state-${lvl}`).className = lvl === currentUserLevel ? 'px-2 py-1.5 rounded bg-[#d70014] text-white transition font-medium' : 'px-2 py-1.5 rounded bg-gray-100 text-gray-700 hover:bg-gray-200 transition';
  662. });
  663. if(isSimPanelOpen) toggleSimPanel();
  664. }
  665. function setDetailBtnState(state) {
  666. currentDetailBtnState = state;
  667. ['enroll', 'progress', 'benefits', 'more'].forEach(s => {
  668. document.getElementById(`btn-detail-${s}`).className = s === state ? 'px-2 py-1.5 rounded bg-[#d70014] text-white transition font-medium' : 'px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition';
  669. });
  670. const mainBtn = document.getElementById('detail-main-btn');
  671. if(state === 'enroll') mainBtn.innerText = '报名';
  672. if(state === 'progress') mainBtn.innerText = '查看进度';
  673. if(state === 'benefits') mainBtn.innerText = '查看权益';
  674. if(state === 'more') mainBtn.innerText = '查看更多';
  675. document.getElementById('enroll-result-control').classList.toggle('hidden', state !== 'enroll');
  676. if(state !== 'enroll' && isEnrollResultPanelOpen) toggleEnrollResultPanel();
  677. if(isDetailBtnPanelOpen) toggleDetailBtnPanel();
  678. }
  679. function setEnrollCondition(cond) {
  680. currentEnrollCondition = cond;
  681. ['success', 'not_eligible', 'full', 'exclusive'].forEach(c => {
  682. document.getElementById(`cond-${c}`).className = c === cond ? 'px-2 py-1.5 rounded bg-[#d70014] text-white transition font-medium' : 'px-2 py-1.5 rounded bg-gray-100 text-gray-700 transition';
  683. });
  684. if(isEnrollResultPanelOpen) toggleEnrollResultPanel();
  685. }
  686. function handleDetailAction() {
  687. if (currentDetailBtnState === 'more') {
  688. closeActivityDetail();
  689. showAllActivities();
  690. } else if (currentDetailBtnState === 'progress') {
  691. showToast('正在为您拉取达标进度...');
  692. } else if (currentDetailBtnState === 'benefits') {
  693. showToast('正在为您拉取权益信息...');
  694. } else if (currentDetailBtnState === 'enroll') {
  695. if (currentEnrollCondition === 'success') {
  696. document.getElementById('confirm-modal-backdrop').classList.remove('opacity-0', 'pointer-events-none');
  697. document.getElementById('confirm-modal').classList.remove('translate-y-full');
  698. } else {
  699. let msg = currentEnrollCondition === 'not_eligible' ? '您暂不符合报名条件' : currentEnrollCondition === 'full' ? '很遗憾,该活动报名已满。' : '您已参与同类型活动,不可重复报名 (互斥)';
  700. document.getElementById('alert-modal-msg').innerText = msg;
  701. document.getElementById('alert-modal-backdrop').classList.remove('opacity-0', 'pointer-events-none');
  702. document.getElementById('alert-modal').classList.remove('translate-y-full');
  703. }
  704. }
  705. }
  706. function openRuleModal() { document.getElementById('rule-modal-backdrop').classList.remove('opacity-0', 'pointer-events-none'); document.getElementById('rule-modal').classList.remove('translate-y-full'); }
  707. function closeRuleModal() { document.getElementById('rule-modal-backdrop').classList.add('opacity-0', 'pointer-events-none'); document.getElementById('rule-modal').classList.add('translate-y-full'); }
  708. function openBalanceRuleModal() { document.getElementById('balance-rule-modal-backdrop').classList.remove('opacity-0', 'pointer-events-none'); document.getElementById('balance-rule-modal').classList.remove('translate-y-full'); }
  709. function closeBalanceRuleModal() { document.getElementById('balance-rule-modal-backdrop').classList.add('opacity-0', 'pointer-events-none'); document.getElementById('balance-rule-modal').classList.add('translate-y-full'); }
  710. function closeAlertModal() { document.getElementById('alert-modal-backdrop').classList.add('opacity-0', 'pointer-events-none'); document.getElementById('alert-modal').classList.add('translate-y-full'); }
  711. function closeConfirmModal() { document.getElementById('confirm-modal-backdrop').classList.add('opacity-0', 'pointer-events-none'); document.getElementById('confirm-modal').classList.add('translate-y-full'); }
  712. let toastTimeout;
  713. function showToast(msg) {
  714. const toast = document.getElementById('toast');
  715. toast.innerText = msg;
  716. toast.classList.remove('opacity-0');
  717. clearTimeout(toastTimeout);
  718. toastTimeout = setTimeout(() => { toast.classList.add('opacity-0'); }, 2000);
  719. }
  720. </script>
  721. </body>
  722. </html>