index.html 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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">
  6. <title>S3 活动首页</title>
  7. <script src="./js/tailwindcss.min.js"></script>
  8. <style>
  9. body { font-family: 'Roboto', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif; }
  10. .content-bg {
  11. background: linear-gradient(180deg, #7aedff 0%, #047200 100%);
  12. min-height: 100vh;
  13. }
  14. .logo-bg {
  15. background-image: url('./static/logo/jbs.png');
  16. background-repeat: no-repeat;
  17. background-position: center;
  18. background-size: contain;
  19. }
  20. </style>
  21. </head>
  22. <body class="bg-gray-100">
  23. <div class="content-bg w-full flex flex-col relative cursor-pointer">
  24. <!-- Top Bar -->
  25. <div class="w-full flex justify-end p-4 pt-8">
  26. <div class="flex items-center bg-black/30 rounded-xl px-3 py-1 gap-2 min-w-[90px] h-[40px]">
  27. <img src="./static/common/clock.png" class="w-5 h-5 object-contain">
  28. <div id="countdown" class="text-white text-lg font-bold text-center min-w-[60px]">--:--</div>
  29. </div>
  30. </div>
  31. <!-- Main Content -->
  32. <div class="flex-1 flex flex-col items-center justify-center gap-6 -mt-20">
  33. <!-- Logo -->
  34. <div class="w-[50vw] h-[50vw] logo-bg"></div>
  35. <!-- Type & Notice -->
  36. <div class="relative flex items-center justify-center w-full">
  37. <img id="notice-icon" src="./static/common/notice.png" class="absolute left-10 w-4 h-4 hidden" alt="notice">
  38. <span id="activity-type" class="text-white/60 text-xl font-bold">锦标赛</span>
  39. </div>
  40. <!-- Name -->
  41. <div id="activity-name" class="text-white text-2xl font-bold text-center px-4">正在加载...</div>
  42. <!-- Button -->
  43. <button id="action-btn" class="bg-white text-black text-xl font-bold py-3 px-12 rounded-full shadow-lg active:scale-95 transition-transform mt-4">
  44. 开始比赛
  45. </button>
  46. </div>
  47. </div>
  48. <!-- Scripts -->
  49. <script src="./js/utils.js"></script>
  50. <script src="./js/bridge.js"></script>
  51. <script src="./js/api.js"></script>
  52. <script>
  53. const state = {
  54. ecId: 0,
  55. isJoin: false,
  56. isFinished: false,
  57. secondCardName: '',
  58. beginSecond: 0,
  59. endSecond: 0,
  60. timer: null,
  61. rankKey: ''
  62. };
  63. window.onload = function() {
  64. const token = Tools.getQueryParam('token');
  65. const xid = Tools.getQueryParam('xid');
  66. state.ecId = Tools.getQueryParam('id') || 0;
  67. if (xid) state.ecId = xid;
  68. state.rankKey = `rank-tpl-style3-${state.ecId}`;
  69. if (window.API) {
  70. API.setToken(token);
  71. if (Tools.getQueryParam('env') === 'mock') {
  72. API.init({ useMock: true });
  73. if (!state.ecId) state.ecId = 'mock_id';
  74. }
  75. }
  76. const typeParam = Tools.getQueryParam('type');
  77. if (typeParam) document.getElementById('activity-type').innerText = decodeURIComponent(typeParam);
  78. const btnTextParam = Tools.getQueryParam('btnText');
  79. if (btnTextParam) document.getElementById('action-btn').innerText = decodeURIComponent(btnTextParam);
  80. const pageArea = document.querySelector('.content-bg');
  81. if (pageArea) {
  82. pageArea.addEventListener('click', handleBtnClick);
  83. }
  84. loadCardBase();
  85. checkUserJoin();
  86. loadMatchDetail();
  87. };
  88. window.onunload = function() {
  89. if (state.timer) {
  90. clearInterval(state.timer);
  91. }
  92. const pageArea = document.querySelector('.content-bg');
  93. if (pageArea) {
  94. pageArea.removeEventListener('click', handleBtnClick);
  95. }
  96. };
  97. function loadCardBase() {
  98. if (!window.API) return;
  99. API.getCardBase(state.ecId).then(res => {
  100. if (res) {
  101. document.getElementById('activity-name').innerText = res.ecName || '未命名活动';
  102. state.beginSecond = res.beginSecond;
  103. state.endSecond = res.endSecond;
  104. state.secondCardName = res.secondCardName;
  105. startCountdown();
  106. }
  107. });
  108. }
  109. function checkUserJoin() {
  110. if (!window.API) return;
  111. API.getUserJoinStatus(state.ecId).then(res => {
  112. if (res && res.isJoin) {
  113. state.isJoin = true;
  114. }
  115. });
  116. }
  117. function loadMatchDetail() {
  118. if (!window.API) return;
  119. API.getMatchRsDetail(state.ecId,0).then(res => {
  120. if (!res) return;
  121. const rankStr = JSON.stringify(res);
  122. const cache = localStorage.getItem(state.rankKey);
  123. if (cache !== rankStr) {
  124. document.getElementById('notice-icon').classList.remove('hidden');
  125. localStorage.setItem(state.rankKey, rankStr);
  126. }
  127. });
  128. }
  129. function startCountdown() {
  130. const countdownEl = document.getElementById('countdown');
  131. const update = () => {
  132. if (state.endSecond > 0) {
  133. const diff = state.endSecond - Date.now() / 1000;
  134. if (diff > 0) {
  135. countdownEl.innerText = '距结束' + Tools.convertSecondsToDHM(diff);
  136. } else {
  137. countdownEl.innerText = '已结束';
  138. state.isFinished = true;
  139. clearInterval(state.timer);
  140. }
  141. }
  142. };
  143. update();
  144. state.timer = setInterval(update, 60000);
  145. }
  146. function handleBtnClick() {
  147. //const queryStr = window.location.search;
  148. const queryStr='?token='+ Tools.getQueryParam('token') +'&id='+ state.ecId;
  149. if (state.isJoin) {
  150. const target = `./ranklist.html${queryStr}&full=true`;
  151. Bridge.appAction(target);
  152. } else {
  153. if (!state.isFinished) {
  154. if (state.secondCardName === 'rankList') {
  155. const target = `./ranklist.html${queryStr}&full=true`;
  156. Bridge.appAction(target);
  157. } else {
  158. const target = `./signup.html${queryStr}&full=true`;
  159. Bridge.appAction(target);
  160. }
  161. } else {
  162. const target = `./ranklist.html${queryStr}&full=true`;
  163. Bridge.appAction(target);
  164. }
  165. }
  166. }
  167. </script>
  168. </body>
  169. </html>