| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>S3 排行榜</title>
- <script src="https://cdn.tailwindcss.com"></script>
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
- <style>
- @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700;900&display=swap');
- @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@500;700&display=swap');
- body { font-family: 'Noto Sans SC', sans-serif; }
- .page-top {
- background-image: url('./static/backgroud/top_bg_sddx.png');
- background-repeat: no-repeat;
- background-position: center;
- background-size: cover;
- min-height: 220px;
- border-bottom-left-radius: 20px;
- border-bottom-right-radius: 20px;
- }
- .stat-box {
- background: #9A300E;
- border: 1px solid #D3A254;
- border-radius: 6px;
- width: 45%;
- padding: 8px;
- text-align: center;
- }
- .stat-label { color: #f3d809; font-size: 12px; font-weight: 500; }
- .stat-value { color: #f3d809; font-size: 16px; font-weight: 700; font-family: 'Roboto', sans-serif; }
- .top-btn { background-color: #9A300E; border: 1px solid #D3A254; border-radius: 6px; padding: 4px 12px; color: white; font-size: 12px; font-weight: 500; }
- .tab-active { color: var(--tab-color, #81cd00); border-bottom: 2px solid var(--tab-color, #81cd00); font-weight: bold; }
- .tab-inactive { color: #666; }
- .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); }
- .sub-tab-inactive { background-color: #f3f4f6; color: #666; }
- .rank-item { transition: transform 0.1s; }
- .rank-item:active { transform: scale(0.99); }
- .rank-badge { width: 32px; text-align: center; }
- .myself-highlight { background-color: #f0f9eb; border: 1px solid #81cd00; }
- .no-scrollbar::-webkit-scrollbar { display: none; }
- </style>
- </head>
- <body class="bg-gray-50 min-h-screen flex flex-col pb-20">
- <!-- 1. 顶部区域 -->
- <div class="page-top w-full relative flex flex-col pt-8 px-4 pb-4">
- <!-- NavBar -->
- <div class="flex justify-between items-center text-white mb-4">
- <button onclick="handleBack()" class="w-8 h-8 flex items-center justify-center bg-black/20 rounded-full active:scale-90 transition">
- <i class="fas fa-chevron-left"></i>
- </button>
- <div class="text-lg font-bold drop-shadow-md" id="mc-name">加载中...</div>
- <div class="flex gap-3">
- <button onclick="handleMessage()" class="relative w-8 h-8 flex items-center justify-center bg-black/20 rounded-full active:scale-90 transition">
- <i class="fas fa-bell"></i>
- <span id="msg-dot" class="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full hidden"></span>
- </button>
- <button onclick="handleInfo()" class="w-8 h-8 flex items-center justify-center bg-black/20 rounded-full active:scale-90 transition">
- <i class="fas fa-question"></i>
- </button>
- </div>
- </div>
- <!-- Stats Dashboard -->
- <div class="flex justify-between items-center mb-2">
- <div class="stat-box flex flex-col">
- <span class="stat-label">赛事总里程</span>
- <span class="stat-value" id="total-distance">-- km</span>
- </div>
- <div class="stat-box flex flex-col">
- <span class="stat-label" id="label-answer">文化输出</span>
- <span class="stat-value" id="total-answer">-- 次</span>
- </div>
- </div>
- <!-- Today Date -->
- <div class="text-center mb-auto">
- <span id="today-date" class="text-[#751f00] font-black text-lg drop-shadow-sm" style="-webkit-text-stroke: 0.5px #DCA452;">--</span>
- </div>
- <!-- User Action Bar -->
- <div class="flex justify-between items-end mt-auto">
- <button class="top-btn" onclick="handleMyTicket()" id="label-ticket">我的奖券</button>
- <span class="top-btn bg-opacity-100 font-bold text-sm px-4" id="my-nickname">--</span>
- <button class="top-btn" onclick="handleExchange()" id="label-exchange">领奖地址</button>
- </div>
- </div>
- <!-- 2. Stats Bar (Additional) -->
- <div class="bg-[#d8e8c6] text-[#3d6706] text-xs py-1 px-2 flex justify-around font-medium">
- <span>题目: <span id="bar-answer">0</span></span>
- <span>里程: <span id="bar-distance">0</span>km</span>
- <span>打点: <span id="bar-cp">0</span></span>
- <span>百味豆: <span id="bar-point">0</span></span>
- </div>
- <!-- 3. Tabs Area -->
- <div class="bg-white sticky top-0 z-30 shadow-sm">
- <!-- Level 1 Tab -->
- <div class="flex border-b border-gray-100">
- <button onclick="switchTab1(0)" id="tab1-0" class="flex-1 py-3 text-sm font-bold tab-active">团体</button>
- <div class="relative flex-1">
- <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">
- <span id="current-map-name">个人</span>
- <i class="fas fa-caret-down text-xs"></i>
- </button>
- <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>
- </div>
- </div>
- <!-- Level 2 Tab -->
- <div class="flex overflow-x-auto gap-3 p-3 no-scrollbar" id="metric-tabs-container"></div>
- </div>
- <!-- 4. List Container -->
- <div id="rank-list-container" class="px-3 pt-3 flex flex-col gap-2 pb-24 min-h-[300px]">
- <div class="text-center text-gray-400 py-10 text-sm">加载中...</div>
- </div>
- <!-- Debug Overlay (仅 debug=1 时展示) -->
- <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>
- <!-- 5. Bottom Action -->
- <div class="fixed bottom-0 w-full bg-white border-t border-gray-100 p-4 z-40 safe-area-bottom">
- <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">
- 加载中...
- </button>
- </div>
- <!-- 简单弹窗 -->
- <div id="infoModal" class="fixed inset-0 z-50 hidden flex items-center justify-center">
- <div class="absolute inset-0 bg-black/60 backdrop-blur-sm" onclick="closeModal('infoModal')"></div>
- <div class="bg-white w-[85%] rounded-xl p-6 relative z-10 max-h-[70vh] overflow-y-auto">
- <h3 class="text-lg font-bold mb-4 text-center border-b pb-2">规则说明</h3>
- <div id="info-content" class="text-sm text-gray-600 leading-relaxed space-y-2"></div>
- <button onclick="closeModal('infoModal')" class="mt-6 w-full py-2 bg-gray-100 rounded-lg font-bold text-gray-600">关闭</button>
- </div>
- </div>
- <div id="msgModal" class="fixed inset-0 z-50 hidden flex items-center justify-center">
- <div class="absolute inset-0 bg-black/60 backdrop-blur-sm" onclick="closeModal('msgModal')"></div>
- <div class="bg-white w-[85%] rounded-xl p-6 relative z-10">
- <h3 class="text-lg font-bold mb-4 text-center">消息通知</h3>
- <div id="msg-content" class="text-sm text-gray-600 mb-4">暂无新消息</div>
- <button onclick="closeModal('msgModal')" class="w-full py-2 bg-[#2e85ec] text-white rounded-lg font-bold">我知道了</button>
- </div>
- </div>
- <script src="./js/utils.js"></script>
- <script src="./js/bridge.js"></script>
- <script src="./js/api.js"></script>
- <script>
- const STATE = {
- ecId: 0,
- token: '',
- mcId: 0,
- mcType: 0,
- mcIdListStr: '',
- ocaId: 0,
- isJoin: false,
- mcState: 0,
- mapList: [],
- rankData: {},
- today: '',
- tab1Current: 0,
- tab2Current: 0,
- mapKey: 'rank-tpl-style3-map',
- rankKey: 'rank-tpl-style3',
- messageKey: 'message-tpl-style3',
- configParam: {
- subTitle: '',
- labelRightAnswerNum: '文化输出',
- labelTicketName: '我的奖券',
- labelAwardAddress: '领奖地址',
- labelGoodsList: '',
- tab1InitActIndex: 0,
- tab2InitActIndex: 0
- },
- dispArrStr: 'teamCp,teamTodayCp,teamDistance,teamRightAnswerPer,teamTodayPace,regionCp,regionTodayCp,regionDistance,regionRightAnswerPer,regionTodayPace',
- tab2ItemsTeam: ['总积分', '今日积分', '总里程', '校园文化', '今日配速'],
- tab2ItemsPerson: ['总积分', '今日积分', '总里程', '校园文化', '今日配速'],
- rankKeysTeam: ['teamCpRs', 'teamTodayCpRs', 'teamDistanceRs', 'teamRightAnswerPerRs', 'teamTodayPaceRs'],
- rankKeysPerson: ['regionCpRs', 'regionTodayCpRs', 'regionDistanceRs', 'regionRightAnswerPerRs', 'regionTodayPaceRs'],
- rankTypesTeam: ['totalScore', 'totalScore', 'totalDistance', 'rightAnswerPer', 'fastPace'],
- rankTypesPerson: ['totalScore', 'totalScore', 'totalDistance', 'rightAnswerPer', 'fastPace']
- };
- function injectCss(css) {
- if (!css) return;
- const style = document.createElement('style');
- style.innerHTML = css;
- document.head.appendChild(style);
- }
- function setListMessage(text, color = 'text-gray-400') {
- document.getElementById('rank-list-container').innerHTML = `<div class="text-center ${color} py-10 text-sm">${text}</div>`;
- }
- function normalizeOcaId(val) {
- if (val === null || val === undefined) return 0;
- if (typeof val === 'number') return val;
- if (typeof val === 'string') {
- const trimmed = val.trim();
- if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
- try {
- const parsed = JSON.parse(trimmed);
- if (parsed && typeof parsed === 'object' && parsed.ocaId) return parsed.ocaId;
- } catch (e) {}
- }
- const num = Number(trimmed);
- return Number.isNaN(num) ? 0 : num;
- }
- if (typeof val === 'object' && val.ocaId) return val.ocaId;
- return 0;
- }
- function initPage() {
- STATE.today = Tools.timestampToTime(Date.now() / 1000, 2);
- document.getElementById('today-date').innerText = STATE.today;
- loadCardConfig();
- matchRsDetailQuery();
- getUserJoin();
- getUnreadMessage();
- }
- window.onload = function() {
- STATE.token = Tools.getQueryParam('token') || '';
- STATE.ecId = Tools.getQueryParam('id') || 112;
- STATE.ocaId = normalizeOcaId(localStorage.getItem(`${STATE.mapKey}-${STATE.ecId}`) || 0);
- STATE.rankKey = `${STATE.rankKey}-${STATE.ecId}`;
- STATE.mapKey = `${STATE.mapKey}-${STATE.ecId}`;
- STATE.messageKey = `${STATE.messageKey}-${STATE.ecId}`;
- if (window.API) {
- if (Tools.getQueryParam('env') === 'mock') {
- API.init({ useMock: true });
- if (!STATE.ecId) STATE.ecId = 'mock_default_id';
- }
- if (STATE.token) {
- API.setToken(STATE.token);
- } else if (window.Bridge && Bridge.getToken) {
- Bridge.onToken((tk) => {
- STATE.token = tk || '';
- API.setToken(STATE.token);
- initPage();
- });
- Bridge.getToken();
- return;
- }
- }
- initPage();
- };
- function loadCardConfig() {
- if (!window.API) return;
- API.getCardConfig(STATE.ecId, 'rankList').then(res => {
- let cfg = res;
- if (res && res.configJson) {
- try { cfg = JSON.parse(res.configJson); } catch (e) { console.warn('config parse fail', e); }
- }
- if (!cfg) return;
- if (cfg.common && cfg.common.css) injectCss(cfg.common.css);
- const pageCfg = cfg.rankList || cfg.ranklist || cfg.rank_list || cfg;
- if (pageCfg && pageCfg.css) injectCss(pageCfg.css);
- if (pageCfg && pageCfg.param) {
- STATE.configParam = Object.assign(STATE.configParam, pageCfg.param);
- STATE.tab1Current = STATE.configParam.tab1InitActIndex || 0;
- STATE.tab2Current = STATE.configParam.tab2InitActIndex || 0;
- }
- if (pageCfg && pageCfg.rankParam) {
- const r = pageCfg.rankParam;
- if (r.dispArrStr) STATE.dispArrStr = r.dispArrStr;
- if (r.tab2Items_team) STATE.tab2ItemsTeam = r.tab2Items_team;
- if (r.tab2Items_person_region) STATE.tab2ItemsPerson = r.tab2Items_person_region;
- if (r.rank1List) STATE.rankKeysTeam = r.rank1List;
- if (r.rank2List) STATE.rankKeysPerson = r.rank2List;
- if (r.rankTypeList_team) STATE.rankTypesTeam = r.rankTypeList_team;
- if (r.rankTypeList_person_region) STATE.rankTypesPerson = r.rankTypeList_person_region;
- }
- document.documentElement.style.setProperty('--tab-color', cfg.common?.tabActiveColor || '#81cd00');
- document.getElementById('label-answer').innerText = STATE.configParam.labelRightAnswerNum;
- document.getElementById('label-ticket').innerText = STATE.configParam.labelTicketName || '我的奖券';
- document.getElementById('label-exchange').innerText = STATE.configParam.labelGoodsList || STATE.configParam.labelAwardAddress || '领奖地址';
- renderTabs();
- });
- }
- function matchRsDetailQuery() {
- if (!window.API) return;
- API.getMatchRsDetail(STATE.ecId, STATE.ocaId).then(res => {
- if (!res) return;
- STATE.mcType = res.mcType;
- STATE.mcId = res.mcId;
- STATE.mcIdListStr = res.mcIdListStr || res.mcId || '';
- STATE.mcName = res.mcName;
- STATE.beginSecond = res.beginSecond;
- STATE.endSecond = res.endSecond;
- STATE.nickName = res.nickName;
- STATE.mcState = Tools.checkMcState(STATE.beginSecond, STATE.endSecond);
- document.getElementById('mc-name').innerText = STATE.mcName || '赛事';
- document.getElementById('my-nickname').innerText = STATE.nickName || '游客';
- mapListQuery();
- compStatisticQuery();
- }).catch(err => {
- console.error('matchRsDetailQuery failed', err);
- setListMessage('数据加载失败,请检查网络或参数'+STATE.ecId+'|'+STATE.ocaId, 'text-red-400');
- });
- }
- function mapListQuery() {
- if (!window.API || !STATE.mcId) {
- setListMessage('缺少赛事信息,无法加载排行榜', 'text-red-400');
- return;
- }
- API.getMapList(STATE.mcId).then(res => {
- if (res && res.length > 0) {
- STATE.mapList = res;
- if (!STATE.ocaId) {
- STATE.ocaId = res[0].ocaId || res[0].mapId || 0;
- }
- renderMapDropdown();
- }
- loadRankData();
- }).catch(err => {
- console.error('mapListQuery failed', err);
- loadRankData();
- });
- }
- function compStatisticQuery() {
- if (!window.API || !STATE.mcId) return;
- API.getCompStatistic(STATE.mcId).then(res => {
- if (!res) return;
- const distKm = Tools.fmtDistance(res.totalDistance);
- document.getElementById('total-distance').innerText = `${distKm} km`;
- document.getElementById('total-answer').innerText = `${res.totalAnswerNum || 0} 次`;
- document.getElementById('bar-answer').innerText = res.totalAnswerNum || 0;
- document.getElementById('bar-distance').innerText = distKm;
- document.getElementById('bar-cp').innerText = res.totalCp || 0;
- document.getElementById('bar-point').innerText = res.totalSysPoint || 0;
- });
- }
- function loadRankData() {
- if (!window.API || !STATE.mcId) return;
- setListMessage('加载中...');
- const idParam = STATE.mcIdListStr || STATE.mcId;
- API.getRankDetail(idParam, STATE.mcType, 227, STATE.dispArrStr).then(res => {
- STATE.rankData = res || {};
- renderList();
- }).catch(() => {
- setListMessage('加载失败', 'text-red-400');
- });
- }
- function getUserJoin() {
- if (!window.API) return;
- API.getUserJoinStatus(STATE.ecId).then(res => {
- if (res) {
- STATE.isJoin = res.isJoin;
- document.getElementById('btn-start').innerText = res.isJoin ? '选择场地' : '我要报名';
- }
- });
- }
- function getUnreadMessage() {
- if (!window.API) return;
- API.getUnReadMessages(STATE.ecId).then(res => {
- if (res && res.length > 0) document.getElementById('msg-dot').classList.remove('hidden');
- });
- }
- function renderTabs() {
- const container = document.getElementById('metric-tabs-container');
- container.innerHTML = '';
- const type = STATE.tab1Current === 0 ? 'team' : 'person';
- const labels = type === 'team' ? STATE.tab2ItemsTeam : STATE.tab2ItemsPerson;
- labels.forEach((label, index) => {
- const btn = document.createElement('button');
- const isActive = index === STATE.tab2Current;
- btn.className = `px-4 py-1.5 rounded-full text-xs whitespace-nowrap transition-all ${isActive ? 'sub-tab-active' : 'sub-tab-inactive'}`;
- btn.innerText = label;
- btn.onclick = () => switchTab2(index);
- container.appendChild(btn);
- });
- document.getElementById('tab1-0').className = `flex-1 py-3 text-sm font-bold ${STATE.tab1Current === 0 ? 'tab-active' : 'tab-inactive'}`;
- 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'}`;
- }
- function renderMapDropdown() {
- const container = document.getElementById('map-dropdown');
- container.innerHTML = '';
- STATE.mapList.forEach(map => {
- const div = document.createElement('div');
- div.className = 'p-3 text-sm text-gray-700 hover:bg-gray-50 cursor-pointer border-b border-gray-50 last:border-none';
- div.innerText = map.mapName || '地图';
- div.onclick = () => selectMap(map);
- container.appendChild(div);
- });
- const current = STATE.mapList.find(m => m.ocaId == STATE.ocaId);
- document.getElementById('current-map-name').innerText = current ? `个人(${current.mapName})` : '个人';
- }
- function renderList() {
- const container = document.getElementById('rank-list-container');
- container.innerHTML = '';
- const isTeam = STATE.tab1Current === 0;
- const dataKey = (isTeam ? STATE.rankKeysTeam : STATE.rankKeysPerson)[STATE.tab2Current] || '';
- const rankType = (isTeam ? STATE.rankTypesTeam : STATE.rankTypesPerson)[STATE.tab2Current] || '';
- const listData = STATE.rankData ? (STATE.rankData[dataKey] || []) : [];
- if (!listData || listData.length === 0) {
- container.innerHTML = '<div class="text-center text-gray-400 py-10 text-sm">暂无排行数据</div>';
- return;
- }
- listData.forEach((item, index) => {
- const rankNum = item.rankNum || index + 1;
- let value = item.inRankNum ?? item.score ?? 0;
- let unit = '';
- if (rankType === 'totalDistance') {
- value = Tools.fmtDistance(value);
- unit = 'km';
- } else if (rankType === 'fastPace') {
- value = Tools.convertSecondsToHMS(value, 2);
- } else if (rankType === 'rightAnswerPer') {
- unit = '%';
- }
- const name = item.teamName || item.userName || '匿名';
- const badge = rankNum <= 3
- ? `<i class="fas fa-medal ${['text-yellow-500','text-gray-400','text-orange-600'][rankNum-1]} text-xl"></i>`
- : `<span class="font-bold text-gray-400">${rankNum}</span>`;
- const row = document.createElement('div');
- 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`;
- row.innerHTML = `
- <div class="flex items-center gap-3 flex-1 min-w-0">
- <div class="rank-badge">${badge}</div>
- <div class="flex flex-col min-w-0">
- <div class="font-bold text-gray-800 truncate text-sm">
- ${item.isSelf ? '<span class="text-blue-600 mr-1">[我]</span>' : ''}${name}
- </div>
- </div>
- </div>
- <div class="text-right shrink-0 ml-2">
- <div class="font-bold text-gray-700 font-mono text-base">${value} <span class="text-xs font-normal text-gray-400">${unit}</span></div>
- </div>
- `;
- container.appendChild(row);
- });
- }
- function switchTab1(index) {
- STATE.tab1Current = index;
- STATE.tab2Current = 0;
- renderTabs();
- renderList();
- }
- function switchTab2(index) {
- STATE.tab2Current = index;
- renderTabs();
- renderList();
- }
- function toggleMapDropdown() {
- if (STATE.tab1Current !== 1) {
- switchTab1(1);
- }
- document.getElementById('map-dropdown').classList.toggle('hidden');
- }
- function selectMap(map) {
- STATE.ocaId = map.ocaId || map.mapId || 0;
- localStorage.setItem(STATE.mapKey, STATE.ocaId);
- document.getElementById('map-dropdown').classList.add('hidden');
- renderMapDropdown();
- loadRankData();
- }
- function handleBack() {
- Bridge.appAction('action://to_home/');
- }
- function handleMessage() {
- document.getElementById('msgModal').classList.remove('hidden');
- document.getElementById('msg-dot').classList.add('hidden');
- }
- function handleInfo() {
- const info = STATE.configParam.subTitle || '暂无规则描述';
- document.getElementById('info-content').innerHTML = `<p>${info}</p>`;
- document.getElementById('infoModal').classList.remove('hidden');
- }
- function handleMyTicket() {
- const qs = window.location.search || `?id=${STATE.ecId}&token=${STATE.token}`;
- Bridge.appAction(`/pages/achievement/index2?tabCurrent=2${qs.replace('?', '&')}`, 'uni.navigateTo');
- }
- function handleExchange() {
- const qs = window.location.search || `?id=${STATE.ecId}&token=${STATE.token}`;
- if (STATE.configParam.labelGoodsList) {
- Bridge.appAction(`/pages/exchange/style1/goodsList${qs.replace('?', '&')}`, 'uni.navigateTo');
- } else {
- Tools.showToast('请在客户端内查看兑换入口');
- }
- }
- function handleStartGame() {
- const qs = window.location.search || `?id=${STATE.ecId}&token=${STATE.token}`;
- if (STATE.isJoin) {
- Bridge.appAction(`./rankOverview.html${qs}`);
- } else {
- Bridge.appAction(`./signup.html${qs}`);
- }
- }
- function closeModal(id) {
- document.getElementById(id).classList.add('hidden');
- }
- </script>
- </body>
- </html>
|