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