ranklist - Copy.html 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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="https://cdn.tailwindcss.com"></script>
  8. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
  9. <style>
  10. @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700;900&display=swap');
  11. @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@500;700&display=swap');
  12. body { font-family: 'Noto Sans SC', sans-serif; }
  13. .page-top {
  14. background-image: url('./static/backgroud/top_bg_sddx.png');
  15. background-repeat: no-repeat;
  16. background-position: center;
  17. background-size: cover;
  18. min-height: 220px;
  19. border-bottom-left-radius: 20px;
  20. border-bottom-right-radius: 20px;
  21. }
  22. .stat-box {
  23. background: #9A300E;
  24. border: 1px solid #D3A254;
  25. border-radius: 6px;
  26. width: 45%;
  27. padding: 8px;
  28. text-align: center;
  29. }
  30. .stat-label { color: #f3d809; font-size: 12px; font-weight: 500; }
  31. .stat-value { color: #f3d809; font-size: 16px; font-weight: 700; font-family: 'Roboto', sans-serif; }
  32. .top-btn { background-color: #9A300E; border: 1px solid #D3A254; border-radius: 6px; padding: 4px 12px; color: white; font-size: 12px; font-weight: 500; }
  33. .tab-active { color: var(--tab-color, #81cd00); border-bottom: 2px solid var(--tab-color, #81cd00); font-weight: bold; }
  34. .tab-inactive { color: #666; }
  35. .sub-tab-active { background-color: var(--tab-color, #81cd00); color: white; font-weight: bold; box-shadow: 0 2px 4px rgba(129, 205, 0, 0.3); }
  36. .sub-tab-inactive { background-color: #f3f4f6; color: #666; }
  37. .rank-item { transition: transform 0.1s; }
  38. .rank-item:active { transform: scale(0.99); }
  39. .rank-badge { width: 32px; text-align: center; }
  40. .myself-highlight { background-color: #f0f9eb; border: 1px solid #81cd00; }
  41. .no-scrollbar::-webkit-scrollbar { display: none; }
  42. </style>
  43. </head>
  44. <body class="bg-gray-50 min-h-screen flex flex-col pb-20">
  45. <!-- 1. 顶部区域 -->
  46. <div class="page-top w-full relative flex flex-col pt-8 px-4 pb-4">
  47. <!-- NavBar -->
  48. <div class="flex justify-between items-center text-white mb-4">
  49. <button onclick="handleBack()" class="w-8 h-8 flex items-center justify-center bg-black/20 rounded-full active:scale-90 transition">
  50. <i class="fas fa-chevron-left"></i>
  51. </button>
  52. <div class="text-lg font-bold drop-shadow-md" id="mc-name">加载中...</div>
  53. <div class="flex gap-3">
  54. <button onclick="handleMessage()" class="relative w-8 h-8 flex items-center justify-center bg-black/20 rounded-full active:scale-90 transition">
  55. <i class="fas fa-bell"></i>
  56. <span id="msg-dot" class="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full hidden"></span>
  57. </button>
  58. <button onclick="handleInfo()" class="w-8 h-8 flex items-center justify-center bg-black/20 rounded-full active:scale-90 transition">
  59. <i class="fas fa-question"></i>
  60. </button>
  61. </div>
  62. </div>
  63. <!-- Stats Dashboard -->
  64. <div class="flex justify-between items-center mb-2">
  65. <div class="stat-box flex flex-col">
  66. <span class="stat-label">赛事总里程</span>
  67. <span class="stat-value" id="total-distance">-- km</span>
  68. </div>
  69. <div class="stat-box flex flex-col">
  70. <span class="stat-label" id="label-answer">文化输出</span>
  71. <span class="stat-value" id="total-answer">-- 次</span>
  72. </div>
  73. </div>
  74. <!-- Today Date -->
  75. <div class="text-center mb-auto">
  76. <span id="today-date" class="text-[#751f00] font-black text-lg drop-shadow-sm" style="-webkit-text-stroke: 0.5px #DCA452;">--</span>
  77. </div>
  78. <!-- User Action Bar -->
  79. <div class="flex justify-between items-end mt-auto">
  80. <button class="top-btn" onclick="handleMyTicket()" id="label-ticket">我的奖券</button>
  81. <span class="top-btn bg-opacity-100 font-bold text-sm px-4" id="my-nickname">--</span>
  82. <button class="top-btn" onclick="handleExchange()" id="label-exchange">领奖地址</button>
  83. </div>
  84. </div>
  85. <!-- 2. Stats Bar (Additional) -->
  86. <div class="bg-[#d8e8c6] text-[#3d6706] text-xs py-1 px-2 flex justify-around font-medium">
  87. <span>题目: <span id="bar-answer">0</span></span>
  88. <span>里程: <span id="bar-distance">0</span>km</span>
  89. <span>打点: <span id="bar-cp">0</span></span>
  90. <span>百味豆: <span id="bar-point">0</span></span>
  91. </div>
  92. <!-- 3. Tabs Area -->
  93. <div class="bg-white sticky top-0 z-30 shadow-sm">
  94. <!-- Level 1 Tab -->
  95. <div class="flex border-b border-gray-100">
  96. <button onclick="switchTab1(0)" id="tab1-0" class="flex-1 py-3 text-sm font-bold tab-active">团体</button>
  97. <div class="relative flex-1">
  98. <button onclick="toggleMapDropdown()" id="tab1-1" class="w-full h-full py-3 text-sm font-bold tab-inactive flex items-center justify-center gap-1">
  99. <span id="current-map-name">个人</span>
  100. <i class="fas fa-caret-down text-xs"></i>
  101. </button>
  102. <div id="map-dropdown" class="absolute top-full left-0 w-full bg-white shadow-lg border border-gray-100 hidden max-h-60 overflow-y-auto z-40 rounded-b-lg no-scrollbar"></div>
  103. </div>
  104. </div>
  105. <!-- Level 2 Tab -->
  106. <div class="flex overflow-x-auto gap-3 p-3 no-scrollbar" id="metric-tabs-container"></div>
  107. </div>
  108. <!-- 4. List Container -->
  109. <div id="rank-list-container" class="px-3 pt-3 flex flex-col gap-2 pb-24 min-h-[300px]">
  110. <div class="text-center text-gray-400 py-10 text-sm">加载中...</div>
  111. </div>
  112. <!-- Debug Overlay (仅 debug=1 时展示) -->
  113. <div id="debug-panel" class="hidden fixed bottom-0 left-0 w-full max-h-40 overflow-y-auto text-xs bg-black/70 text-green-300 p-2 z-50"></div>
  114. <!-- 5. Bottom Action -->
  115. <div class="fixed bottom-0 w-full bg-white border-t border-gray-100 p-4 z-40 safe-area-bottom">
  116. <button onclick="handleStartGame()" id="btn-start" class="w-full bg-[#2e85ec] text-white font-bold py-3 rounded-full shadow-lg active:scale-95 transition-transform text-base">
  117. 加载中...
  118. </button>
  119. </div>
  120. <!-- 简单弹窗 -->
  121. <div id="infoModal" class="fixed inset-0 z-50 hidden flex items-center justify-center">
  122. <div class="absolute inset-0 bg-black/60 backdrop-blur-sm" onclick="closeModal('infoModal')"></div>
  123. <div class="bg-white w-[85%] rounded-xl p-6 relative z-10 max-h-[70vh] overflow-y-auto">
  124. <h3 class="text-lg font-bold mb-4 text-center border-b pb-2">规则说明</h3>
  125. <div id="info-content" class="text-sm text-gray-600 leading-relaxed space-y-2"></div>
  126. <button onclick="closeModal('infoModal')" class="mt-6 w-full py-2 bg-gray-100 rounded-lg font-bold text-gray-600">关闭</button>
  127. </div>
  128. </div>
  129. <div id="msgModal" class="fixed inset-0 z-50 hidden flex items-center justify-center">
  130. <div class="absolute inset-0 bg-black/60 backdrop-blur-sm" onclick="closeModal('msgModal')"></div>
  131. <div class="bg-white w-[85%] rounded-xl p-6 relative z-10">
  132. <h3 class="text-lg font-bold mb-4 text-center">消息通知</h3>
  133. <div id="msg-content" class="text-sm text-gray-600 mb-4">暂无新消息</div>
  134. <button onclick="closeModal('msgModal')" class="w-full py-2 bg-[#2e85ec] text-white rounded-lg font-bold">我知道了</button>
  135. </div>
  136. </div>
  137. <script src="./js/utils.js"></script>
  138. <script src="./js/bridge.js"></script>
  139. <script src="./js/api.js"></script>
  140. <script>
  141. const STATE = {
  142. ecId: 0,
  143. token: '',
  144. mcId: 0,
  145. mcType: 0,
  146. mcIdListStr: '',
  147. ocaId: 0,
  148. isJoin: false,
  149. mcState: 0,
  150. mapList: [],
  151. rankData: {},
  152. today: '',
  153. tab1Current: 0,
  154. tab2Current: 0,
  155. mapKey: 'rank-tpl-style3-map',
  156. rankKey: 'rank-tpl-style3',
  157. messageKey: 'message-tpl-style3',
  158. configParam: {
  159. subTitle: '',
  160. labelRightAnswerNum: '文化输出',
  161. labelTicketName: '我的奖券',
  162. labelAwardAddress: '领奖地址',
  163. labelGoodsList: '',
  164. tab1InitActIndex: 0,
  165. tab2InitActIndex: 0
  166. },
  167. dispArrStr: 'teamCp,teamTodayCp,teamDistance,teamRightAnswerPer,teamTodayPace,regionCp,regionTodayCp,regionDistance,regionRightAnswerPer,regionTodayPace',
  168. tab2ItemsTeam: ['总积分', '今日积分', '总里程', '校园文化', '今日配速'],
  169. tab2ItemsPerson: ['总积分', '今日积分', '总里程', '校园文化', '今日配速'],
  170. rankKeysTeam: ['teamCpRs', 'teamTodayCpRs', 'teamDistanceRs', 'teamRightAnswerPerRs', 'teamTodayPaceRs'],
  171. rankKeysPerson: ['regionCpRs', 'regionTodayCpRs', 'regionDistanceRs', 'regionRightAnswerPerRs', 'regionTodayPaceRs'],
  172. rankTypesTeam: ['totalScore', 'totalScore', 'totalDistance', 'rightAnswerPer', 'fastPace'],
  173. rankTypesPerson: ['totalScore', 'totalScore', 'totalDistance', 'rightAnswerPer', 'fastPace']
  174. };
  175. function injectCss(css) {
  176. if (!css) return;
  177. const style = document.createElement('style');
  178. style.innerHTML = css;
  179. document.head.appendChild(style);
  180. }
  181. function setListMessage(text, color = 'text-gray-400') {
  182. document.getElementById('rank-list-container').innerHTML = `<div class="text-center ${color} py-10 text-sm">${text}</div>`;
  183. }
  184. function normalizeOcaId(val) {
  185. if (val === null || val === undefined) return 0;
  186. if (typeof val === 'number') return val;
  187. if (typeof val === 'string') {
  188. const trimmed = val.trim();
  189. if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
  190. try {
  191. const parsed = JSON.parse(trimmed);
  192. if (parsed && typeof parsed === 'object' && parsed.ocaId) return parsed.ocaId;
  193. } catch (e) {}
  194. }
  195. const num = Number(trimmed);
  196. return Number.isNaN(num) ? 0 : num;
  197. }
  198. if (typeof val === 'object' && val.ocaId) return val.ocaId;
  199. return 0;
  200. }
  201. function initPage() {
  202. STATE.today = Tools.timestampToTime(Date.now() / 1000, 2);
  203. document.getElementById('today-date').innerText = STATE.today;
  204. loadCardConfig();
  205. matchRsDetailQuery();
  206. getUserJoin();
  207. getUnreadMessage();
  208. }
  209. window.onload = function() {
  210. STATE.token = Tools.getQueryParam('token') || '';
  211. STATE.ecId = Tools.getQueryParam('id') || 112;
  212. STATE.ocaId = normalizeOcaId(localStorage.getItem(`${STATE.mapKey}-${STATE.ecId}`) || 0);
  213. STATE.rankKey = `${STATE.rankKey}-${STATE.ecId}`;
  214. STATE.mapKey = `${STATE.mapKey}-${STATE.ecId}`;
  215. STATE.messageKey = `${STATE.messageKey}-${STATE.ecId}`;
  216. if (window.API) {
  217. if (Tools.getQueryParam('env') === 'mock') {
  218. API.init({ useMock: true });
  219. if (!STATE.ecId) STATE.ecId = 'mock_default_id';
  220. }
  221. if (STATE.token) {
  222. API.setToken(STATE.token);
  223. } else if (window.Bridge && Bridge.getToken) {
  224. Bridge.onToken((tk) => {
  225. STATE.token = tk || '';
  226. API.setToken(STATE.token);
  227. initPage();
  228. });
  229. Bridge.getToken();
  230. return;
  231. }
  232. }
  233. initPage();
  234. };
  235. function loadCardConfig() {
  236. if (!window.API) return;
  237. API.getCardConfig(STATE.ecId, 'rankList').then(res => {
  238. let cfg = res;
  239. if (res && res.configJson) {
  240. try { cfg = JSON.parse(res.configJson); } catch (e) { console.warn('config parse fail', e); }
  241. }
  242. if (!cfg) return;
  243. if (cfg.common && cfg.common.css) injectCss(cfg.common.css);
  244. const pageCfg = cfg.rankList || cfg.ranklist || cfg.rank_list || cfg;
  245. if (pageCfg && pageCfg.css) injectCss(pageCfg.css);
  246. if (pageCfg && pageCfg.param) {
  247. STATE.configParam = Object.assign(STATE.configParam, pageCfg.param);
  248. STATE.tab1Current = STATE.configParam.tab1InitActIndex || 0;
  249. STATE.tab2Current = STATE.configParam.tab2InitActIndex || 0;
  250. }
  251. if (pageCfg && pageCfg.rankParam) {
  252. const r = pageCfg.rankParam;
  253. if (r.dispArrStr) STATE.dispArrStr = r.dispArrStr;
  254. if (r.tab2Items_team) STATE.tab2ItemsTeam = r.tab2Items_team;
  255. if (r.tab2Items_person_region) STATE.tab2ItemsPerson = r.tab2Items_person_region;
  256. if (r.rank1List) STATE.rankKeysTeam = r.rank1List;
  257. if (r.rank2List) STATE.rankKeysPerson = r.rank2List;
  258. if (r.rankTypeList_team) STATE.rankTypesTeam = r.rankTypeList_team;
  259. if (r.rankTypeList_person_region) STATE.rankTypesPerson = r.rankTypeList_person_region;
  260. }
  261. document.documentElement.style.setProperty('--tab-color', cfg.common?.tabActiveColor || '#81cd00');
  262. document.getElementById('label-answer').innerText = STATE.configParam.labelRightAnswerNum;
  263. document.getElementById('label-ticket').innerText = STATE.configParam.labelTicketName || '我的奖券';
  264. document.getElementById('label-exchange').innerText = STATE.configParam.labelGoodsList || STATE.configParam.labelAwardAddress || '领奖地址';
  265. renderTabs();
  266. });
  267. }
  268. function matchRsDetailQuery() {
  269. if (!window.API) return;
  270. API.getMatchRsDetail(STATE.ecId, STATE.ocaId).then(res => {
  271. if (!res) return;
  272. STATE.mcType = res.mcType;
  273. STATE.mcId = res.mcId;
  274. STATE.mcIdListStr = res.mcIdListStr || res.mcId || '';
  275. STATE.mcName = res.mcName;
  276. STATE.beginSecond = res.beginSecond;
  277. STATE.endSecond = res.endSecond;
  278. STATE.nickName = res.nickName;
  279. STATE.mcState = Tools.checkMcState(STATE.beginSecond, STATE.endSecond);
  280. document.getElementById('mc-name').innerText = STATE.mcName || '赛事';
  281. document.getElementById('my-nickname').innerText = STATE.nickName || '游客';
  282. mapListQuery();
  283. compStatisticQuery();
  284. }).catch(err => {
  285. console.error('matchRsDetailQuery failed', err);
  286. setListMessage('数据加载失败,请检查网络或参数'+STATE.ecId+'|'+STATE.ocaId, 'text-red-400');
  287. });
  288. }
  289. function mapListQuery() {
  290. if (!window.API || !STATE.mcId) {
  291. setListMessage('缺少赛事信息,无法加载排行榜', 'text-red-400');
  292. return;
  293. }
  294. API.getMapList(STATE.mcId).then(res => {
  295. if (res && res.length > 0) {
  296. STATE.mapList = res;
  297. if (!STATE.ocaId) {
  298. STATE.ocaId = res[0].ocaId || res[0].mapId || 0;
  299. }
  300. renderMapDropdown();
  301. }
  302. loadRankData();
  303. }).catch(err => {
  304. console.error('mapListQuery failed', err);
  305. loadRankData();
  306. });
  307. }
  308. function compStatisticQuery() {
  309. if (!window.API || !STATE.mcId) return;
  310. API.getCompStatistic(STATE.mcId).then(res => {
  311. if (!res) return;
  312. const distKm = Tools.fmtDistance(res.totalDistance);
  313. document.getElementById('total-distance').innerText = `${distKm} km`;
  314. document.getElementById('total-answer').innerText = `${res.totalAnswerNum || 0} 次`;
  315. document.getElementById('bar-answer').innerText = res.totalAnswerNum || 0;
  316. document.getElementById('bar-distance').innerText = distKm;
  317. document.getElementById('bar-cp').innerText = res.totalCp || 0;
  318. document.getElementById('bar-point').innerText = res.totalSysPoint || 0;
  319. });
  320. }
  321. function loadRankData() {
  322. if (!window.API || !STATE.mcId) return;
  323. setListMessage('加载中...');
  324. const idParam = STATE.mcIdListStr || STATE.mcId;
  325. API.getRankDetail(idParam, STATE.mcType, 227, STATE.dispArrStr).then(res => {
  326. STATE.rankData = res || {};
  327. renderList();
  328. }).catch(() => {
  329. setListMessage('加载失败', 'text-red-400');
  330. });
  331. }
  332. function getUserJoin() {
  333. if (!window.API) return;
  334. API.getUserJoinStatus(STATE.ecId).then(res => {
  335. if (res) {
  336. STATE.isJoin = res.isJoin;
  337. document.getElementById('btn-start').innerText = res.isJoin ? '选择场地' : '我要报名';
  338. }
  339. });
  340. }
  341. function getUnreadMessage() {
  342. if (!window.API) return;
  343. API.getUnReadMessages(STATE.ecId).then(res => {
  344. if (res && res.length > 0) document.getElementById('msg-dot').classList.remove('hidden');
  345. });
  346. }
  347. function renderTabs() {
  348. const container = document.getElementById('metric-tabs-container');
  349. container.innerHTML = '';
  350. const type = STATE.tab1Current === 0 ? 'team' : 'person';
  351. const labels = type === 'team' ? STATE.tab2ItemsTeam : STATE.tab2ItemsPerson;
  352. labels.forEach((label, index) => {
  353. const btn = document.createElement('button');
  354. const isActive = index === STATE.tab2Current;
  355. btn.className = `px-4 py-1.5 rounded-full text-xs whitespace-nowrap transition-all ${isActive ? 'sub-tab-active' : 'sub-tab-inactive'}`;
  356. btn.innerText = label;
  357. btn.onclick = () => switchTab2(index);
  358. container.appendChild(btn);
  359. });
  360. document.getElementById('tab1-0').className = `flex-1 py-3 text-sm font-bold ${STATE.tab1Current === 0 ? 'tab-active' : 'tab-inactive'}`;
  361. document.getElementById('tab1-1').className = `w-full h-full py-3 text-sm font-bold flex items-center justify-center gap-1 ${STATE.tab1Current === 1 ? 'tab-active' : 'tab-inactive'}`;
  362. }
  363. function renderMapDropdown() {
  364. const container = document.getElementById('map-dropdown');
  365. container.innerHTML = '';
  366. STATE.mapList.forEach(map => {
  367. const div = document.createElement('div');
  368. div.className = 'p-3 text-sm text-gray-700 hover:bg-gray-50 cursor-pointer border-b border-gray-50 last:border-none';
  369. div.innerText = map.mapName || '地图';
  370. div.onclick = () => selectMap(map);
  371. container.appendChild(div);
  372. });
  373. const current = STATE.mapList.find(m => m.ocaId == STATE.ocaId);
  374. document.getElementById('current-map-name').innerText = current ? `个人(${current.mapName})` : '个人';
  375. }
  376. function renderList() {
  377. const container = document.getElementById('rank-list-container');
  378. container.innerHTML = '';
  379. const isTeam = STATE.tab1Current === 0;
  380. const dataKey = (isTeam ? STATE.rankKeysTeam : STATE.rankKeysPerson)[STATE.tab2Current] || '';
  381. const rankType = (isTeam ? STATE.rankTypesTeam : STATE.rankTypesPerson)[STATE.tab2Current] || '';
  382. const listData = STATE.rankData ? (STATE.rankData[dataKey] || []) : [];
  383. if (!listData || listData.length === 0) {
  384. container.innerHTML = '<div class="text-center text-gray-400 py-10 text-sm">暂无排行数据</div>';
  385. return;
  386. }
  387. listData.forEach((item, index) => {
  388. const rankNum = item.rankNum || index + 1;
  389. let value = item.inRankNum ?? item.score ?? 0;
  390. let unit = '';
  391. if (rankType === 'totalDistance') {
  392. value = Tools.fmtDistance(value);
  393. unit = 'km';
  394. } else if (rankType === 'fastPace') {
  395. value = Tools.convertSecondsToHMS(value, 2);
  396. } else if (rankType === 'rightAnswerPer') {
  397. unit = '%';
  398. }
  399. const name = item.teamName || item.userName || '匿名';
  400. const badge = rankNum <= 3
  401. ? `<i class="fas fa-medal ${['text-yellow-500','text-gray-400','text-orange-600'][rankNum-1]} text-xl"></i>`
  402. : `<span class="font-bold text-gray-400">${rankNum}</span>`;
  403. const row = document.createElement('div');
  404. row.className = `rank-item ${item.isSelf ? 'myself-highlight' : 'bg-white'} rounded-xl p-3 flex items-center justify-between shadow-sm border border-gray-100`;
  405. row.innerHTML = `
  406. <div class="flex items-center gap-3 flex-1 min-w-0">
  407. <div class="rank-badge">${badge}</div>
  408. <div class="flex flex-col min-w-0">
  409. <div class="font-bold text-gray-800 truncate text-sm">
  410. ${item.isSelf ? '<span class="text-blue-600 mr-1">[我]</span>' : ''}${name}
  411. </div>
  412. </div>
  413. </div>
  414. <div class="text-right shrink-0 ml-2">
  415. <div class="font-bold text-gray-700 font-mono text-base">${value} <span class="text-xs font-normal text-gray-400">${unit}</span></div>
  416. </div>
  417. `;
  418. container.appendChild(row);
  419. });
  420. }
  421. function switchTab1(index) {
  422. STATE.tab1Current = index;
  423. STATE.tab2Current = 0;
  424. renderTabs();
  425. renderList();
  426. }
  427. function switchTab2(index) {
  428. STATE.tab2Current = index;
  429. renderTabs();
  430. renderList();
  431. }
  432. function toggleMapDropdown() {
  433. if (STATE.tab1Current !== 1) {
  434. switchTab1(1);
  435. }
  436. document.getElementById('map-dropdown').classList.toggle('hidden');
  437. }
  438. function selectMap(map) {
  439. STATE.ocaId = map.ocaId || map.mapId || 0;
  440. localStorage.setItem(STATE.mapKey, STATE.ocaId);
  441. document.getElementById('map-dropdown').classList.add('hidden');
  442. renderMapDropdown();
  443. loadRankData();
  444. }
  445. function handleBack() {
  446. Bridge.appAction('action://to_home/');
  447. }
  448. function handleMessage() {
  449. document.getElementById('msgModal').classList.remove('hidden');
  450. document.getElementById('msg-dot').classList.add('hidden');
  451. }
  452. function handleInfo() {
  453. const info = STATE.configParam.subTitle || '暂无规则描述';
  454. document.getElementById('info-content').innerHTML = `<p>${info}</p>`;
  455. document.getElementById('infoModal').classList.remove('hidden');
  456. }
  457. function handleMyTicket() {
  458. const qs = window.location.search || `?id=${STATE.ecId}&token=${STATE.token}`;
  459. Bridge.appAction(`/pages/achievement/index2?tabCurrent=2${qs.replace('?', '&')}`, 'uni.navigateTo');
  460. }
  461. function handleExchange() {
  462. const qs = window.location.search || `?id=${STATE.ecId}&token=${STATE.token}`;
  463. if (STATE.configParam.labelGoodsList) {
  464. Bridge.appAction(`/pages/exchange/style1/goodsList${qs.replace('?', '&')}`, 'uni.navigateTo');
  465. } else {
  466. Tools.showToast('请在客户端内查看兑换入口');
  467. }
  468. }
  469. function handleStartGame() {
  470. const qs = window.location.search || `?id=${STATE.ecId}&token=${STATE.token}`;
  471. if (STATE.isJoin) {
  472. Bridge.appAction(`./rankOverview.html${qs}`);
  473. } else {
  474. Bridge.appAction(`./signup.html${qs}`);
  475. }
  476. }
  477. function closeModal(id) {
  478. document.getElementById(id).classList.add('hidden');
  479. }
  480. </script>
  481. </body>
  482. </html>