// 导入配置信息
import CONFIG from './config.js';
import { initDisableDevtool } from './devtool-control.js';
// 获取 URL 参数的函数
function getUrlParameter(name) {
// 获取当前 URL 的查询字符串部分
const queryString = window.location.search;
// 创建 URLSearchParams 对象
const urlParams = new URLSearchParams(queryString);
// 返回指定参数的值
return urlParams.get(name);
}
// Parse codetype from URL. Only allow 1/2/3; default to all when missing/invalid.
function getTypeListFromUrl() {
const validTypes = ['1', '2', '3'];
const typeParam = getUrlParameter('type');
if (!typeParam) {
return validTypes.slice();
}
const types = typeParam
.split(',')
.map(t => t.trim())
.filter(t => validTypes.includes(t));
if (types.length === 0) {
return validTypes.slice();
}
return Array.from(new Set(types));
}
/**
* 将UTC时间转换为北京时间(UTC+8)
* @param {string|Date} utcTime - UTC时间字符串或Date对象
* @returns {Date} 转换后的北京时间Date对象
*/
function convertUTCToBeijingTime(utcTime) {
const date = utcTime instanceof Date ? new Date(utcTime) : new Date(utcTime);
// 添加8小时的毫秒数(8小时 = 8 * 60 * 60 * 1000毫秒)
date.setTime(date.getTime() + 8 * 60 * 60 * 1000);
return date;
}
// 添加登录失败计数和锁定时间的变量
let loginFailCount = 0;
let loginLockUntil = 0;
// 存储比赛信息和服务器时间
let competitionInfo = null;
let serverTime = null;
// 添加平台选择引导函数
function showPlatformGuide() {
// 开发测试用:取消下面这行注释可以强制每次都显示引导提示
localStorage.removeItem('platformGuideShown');
// 检查本地存储,看是否已经显示过引导
if (localStorage.getItem('platformGuideShown') === 'true') {
console.log('已经显示过平台选择引导,不再显示');
return; // 如果已经显示过,就不再显示
}
// 获取自定义下拉菜单元素(而非原始下拉菜单)
const customSelectElement = document.querySelector('.custom-select-selected');
if (!customSelectElement) {
// 如果找不到自定义下拉菜单,尝试延迟执行
console.log('找不到自定义下拉菜单,延迟再次尝试');
setTimeout(showPlatformGuide, 500);
return;
}
console.log('找到自定义下拉菜单,显示引导提示');
// 创建提示框元素
const tooltip = document.createElement('div');
tooltip.className = 'platform-guide-tooltip';
// 检查屏幕宽度,在小屏幕上调整位置和文本
const isSmallScreen = window.innerWidth < 768;
// 根据屏幕大小设置不同的提示文本
if (isSmallScreen) {
tooltip.innerHTML = `
×
平台模式选择
👆 点击上方下拉菜单选择:
🚀 训练平台 · 日常练习模式
🏆 考试平台 · 正式比赛模式
`;
} else {
tooltip.innerHTML = `
×
平台模式选择
👉 点击右侧下拉菜单选择:
🚀 训练平台 · 日常练习模式
🏆 考试平台 · 正式比赛模式
`;
}
// 添加到页面(先添加才能获取正确的尺寸)
document.body.appendChild(tooltip);
// 计算位置 - 在下拉框左侧
const selectRect = customSelectElement.getBoundingClientRect();
const tooltipRect = tooltip.getBoundingClientRect();
// 检查屏幕宽度,在小屏幕上调整位置
// const isSmallScreen = window.innerWidth < 768; // 已在上面声明过,这里不需要重复声明
if (isSmallScreen) {
// 在小屏幕上显示在下拉框下方
tooltip.style.position = 'fixed';
tooltip.style.top = (selectRect.bottom + 20) + 'px';
tooltip.style.left = (selectRect.left + (selectRect.width - tooltipRect.width) / 2) + 'px';
// 给提示框添加特殊类,使用不同的箭头样式
tooltip.classList.add('position-bottom');
} else {
// 在大屏幕上显示在左侧
tooltip.style.position = 'fixed';
tooltip.style.top = (selectRect.top + selectRect.height / 2 - tooltipRect.height / 2) + 'px';
tooltip.style.left = (selectRect.left - tooltipRect.width - 20) + 'px';
}
// 确保提示框不会超出屏幕边界
if (parseFloat(tooltip.style.top) < 10) {
tooltip.style.top = '10px';
}
if (parseFloat(tooltip.style.left) < 10) {
tooltip.style.left = '10px';
}
console.log('提示框位置:', tooltip.style.top, tooltip.style.left);
// 添加高亮样式到选择框
customSelectElement.classList.add('highlight-select');
// 添加关闭按钮功能
const closeBtn = tooltip.querySelector('.platform-guide-close');
closeBtn.addEventListener('click', function () {
document.body.removeChild(tooltip);
customSelectElement.classList.remove('highlight-select');
localStorage.setItem('platformGuideShown', 'true');
});
// 点击下拉菜单也会关闭提示
customSelectElement.addEventListener('click', function () {
if (document.body.contains(tooltip)) {
document.body.removeChild(tooltip);
customSelectElement.classList.remove('highlight-select');
localStorage.setItem('platformGuideShown', 'true');
}
});
// 5秒后自动消失
setTimeout(function () {
if (document.body.contains(tooltip)) {
document.body.removeChild(tooltip);
customSelectElement.classList.remove('highlight-select');
localStorage.setItem('platformGuideShown', 'true');
}
}, 8000); // 增加到8秒,给用户更多阅读时间
}
document.addEventListener('DOMContentLoaded', function () {
if (CONFIG.disabledev) {
initDisableDevtool({
ondevtoolopen: () => {
// window.location.href = '/index.html';
// window.location.href = localStorage.getItem('returnUrl');
},
interval: 500,
clearLog: true,
disableMenu: true,
});
}
});
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function () {
// 初始化时不进行隐藏操作,直接显示 body 背景
// const sectionElement = document.querySelector('.u-section-1');
// if (sectionElement) {
// sectionElement.style.visibility = 'hidden';
// }
// 从localStorage恢复登录锁定状态
const savedLockUntil = localStorage.getItem('loginLockUntil');
const savedFailCount = localStorage.getItem('loginFailCount');
if (savedLockUntil) {
loginLockUntil = parseInt(savedLockUntil, 10);
}
if (savedFailCount) {
loginFailCount = parseInt(savedFailCount, 10);
}
// 检查是否处于锁定状态,如果是,显示提示
const currentTime = new Date().getTime();
if (currentTime < loginLockUntil) {
const remainingSeconds = Math.ceil((loginLockUntil - currentTime) / 1000);
showToast(`登录失败次数过多,请在${remainingSeconds}秒后再试`, 'error');
}
// 获取 competition_id 参数
const competitionId = getUrlParameter('competition_id');
// 如果存在 competition_id 参数,则进行相应处理
if (competitionId) {
console.log('比赛ID:', competitionId);
// 更新页面标题
document.title = '比赛 #' + competitionId;
// 先查询比赛信息,等待完成后再处理背景和欢迎图片
fetchCompetitionInfo(competitionId).then(() => {
// 比赛信息加载完成后,如果没有欢迎图片显示,则设置背景图片
// 注意:欢迎图片的显示已经在 fetchCompetitionInfo 内部的 updateCompetitionInfo 函数中处理了
if (!document.querySelector('.welcome-overlay')) {
setBackgroundImage();
}
}).catch(() => {
// 如果获取比赛信息失败,使用默认处理
const welcomeImageUrl = sessionStorage.getItem('competition_welcome_image_url');
if (welcomeImageUrl) {
showWelcomeImageOverlay(welcomeImageUrl);
} else {
setBackgroundImage();
}
});
// 获取服务器时间
getServerTime();
} else {
console.log('未提供比赛ID');
// 处理未提供 competition_id 的情况 - 没有比赛ID时直接处理
const welcomeImageUrl = sessionStorage.getItem('competition_welcome_image_url');
if (welcomeImageUrl) {
showWelcomeImageOverlay(welcomeImageUrl);
} else {
setBackgroundImage();
}
}
// 将切换密码可见性函数添加到全局作用域
window.togglePasswordVisibility = togglePasswordVisibility;
// 为注册表单的密码输入框添加显示/隐藏功能
setupPasswordToggle();
// 直接为提交按钮添加点击事件
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.addEventListener('click', function (event) {
event.preventDefault();
// 获取表单数据
const username = document.getElementById('name-b064').value;
const password = document.getElementById('email-b064').value;
const platformType = document.getElementById('select-58f1').value;
// 验证表单数据
if (!username || !password || !platformType) {
showToast('用户名、密码和平台类型不能为空', 'error');
return;
}
// 验证时间是否允许选择该平台
if (!validateTimeForPlatform(platformType)) {
return; // 验证失败,不继续执行
}
// 检查 type 参数
const types = getTypeListFromUrl();
if (types.length === 1) {
// 只有一个类型,直接登录并传入 codetype
loginUser(username, password, platformType, types[0]);
} else {
// 多个类型或无类型,正常登录
loginUser(username, password, platformType);
}
});
}
// 为模态框中的提交按钮添加点击事件
const modalSubmitButton = document.querySelector('.u-btn-step');
if (modalSubmitButton) {
modalSubmitButton.addEventListener('click', function (event) {
event.preventDefault();
// 获取用户选择的编码类型
const codeTypeSelect = document.getElementById('select-8c9b');
const codeType = codeTypeSelect ? codeTypeSelect.value : '1';
// 获取比赛ID
const competitionId = getUrlParameter('competition_id');
console.error(competitionId);
if (competitionId) {
// 调用加入比赛API
joinCompetition(competitionId, codeType);
} else {
showToast('未找到比赛信息', 'error');
}
});
}
// 为注册账号按钮添加点击事件
const registerButton = document.querySelector('.u-btn-3');
if (registerButton) {
registerButton.addEventListener('click', function () {
// 加载省份、年级等数据
loadProvinces();
loadGrades();
});
}
// 为省份选择框添加变更事件
const provinceSelect = document.getElementById('select-c2b7');
if (provinceSelect) {
provinceSelect.addEventListener('change', function () {
const provinceId = this.value;
if (provinceId && provinceId !== '省') {
loadCities(provinceId);
}
});
}
// 为城市选择框添加变更事件
const citySelect = document.getElementById('select-36ac');
if (citySelect) {
citySelect.addEventListener('change', function () {
const cityId = this.value;
if (cityId && cityId !== '市') {
loadDistricts(cityId);
}
});
}
// 为区县选择框添加变更事件 - 加载学校数据
const districtSelect = document.getElementById('select-4860');
if (districtSelect) {
districtSelect.addEventListener('change', function () {
const districtId = this.value;
if (districtId && districtId !== '区/县') {
// 重置学校搜索数据
if (typeof window.populateSchoolsWithSearchComp === 'function') {
window.populateSchoolsWithSearchComp([]);
}
// 加载学校数据
loadSchoolsByDistrict(districtId);
} else {
// 如果选择默认选项,清空学校数据
if (typeof window.populateSchoolsWithSearchComp === 'function') {
window.populateSchoolsWithSearchComp([]);
}
}
});
}
// 为注册表单提交按钮添加点击事件
const registerSubmitButton = document.querySelector('.u-dialog-section-6 .u-btn-step');
if (registerSubmitButton) {
registerSubmitButton.addEventListener('click', function (event) {
event.preventDefault();
// 调用注册函数
registerUser();
});
}
// 自定义平台选择下拉菜单
setupCustomPlatformSelect();
// 1) 阻止所有 form 的默认提交(包括按 Enter 触发的隐式提交)
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', function (e) {
e.preventDefault();
e.stopPropagation();
});
});
// 2) 在输入框内按 Enter 时,阻止默认行为(不影响 textarea 换行)
document.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
const target = e.target;
if (target && target.tagName === 'INPUT') {
e.preventDefault();
}
}
});
});
/**
* 获取服务器时间
*/
function getServerTime() {
// 构建请求数据
const requestData = {
key: "abc123xyz456" // 访问密钥
};
// 发送POST请求到服务器
fetch(`${CONFIG.DataServerBaseUrl}/api/competition/get_server_time/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
})
.then(response => {
if (!response.ok) {
throw new Error('网络请求失败');
}
return response.json();
})
.then(data => {
console.log('服务器时间:', data);
if (data.success && data.data) {
// 服务器返回的timestamp对应UTC时间,但实际使用时应该按北京时间处理
// 直接使用服务器返回的timestamp,不需要额外转换
serverTime = {
time: data.data.server_time, // 保留原始时间字符串
timestamp: data.data.timestamp // 直接使用服务器时间戳
};
console.log('使用的时间戳:', data.data.timestamp, new Date(data.data.timestamp * 1000).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }));
// 更新平台选择下拉框状态
updatePlatformSelectStatus();
} else {
console.error('获取服务器时间失败:', data.message);
}
})
.catch(error => {
console.error('获取服务器时间请求失败:', error);
});
}
/**
* 根据比赛ID查询比赛信息
* @param {string|number} competitionId - 比赛ID
*/
function fetchCompetitionInfo(competitionId) {
// 获取显示比赛信息的元素
const competitionInfoElement = document.getElementById('competition-info');
if (competitionInfoElement) {
competitionInfoElement.textContent = '正在加载比赛 #' + competitionId + ' 的信息...';
}
// 获取登录按钮
const submitButton = document.querySelector('.u-btn-submit');
// 构建请求数据
const requestData = {
id: parseInt(competitionId, 10) // 确保ID是整数
};
// 发送POST请求到服务器并返回 Promise
return fetch(`${CONFIG.DataServerBaseUrl}/api/competition/query_competitions/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
})
.then(response => {
if (!response.ok) {
throw new Error('网络请求失败');
}
return response.json();
})
.then(data => {
console.log('比赛信息:', data);
if (data.success && data.data && data.data.competitions && data.data.competitions.length > 0) {
// 获取比赛信息
const competition = data.data.competitions[0];
// 存储比赛信息
competitionInfo = competition;
// 更新页面标题为比赛名称
document.title = competition.name;
// 更新比赛信息显示
updateCompetitionInfo(competition);
// 更新平台选择下拉框状态
updatePlatformSelectStatus();
// 启用登录按钮
if (submitButton) {
submitButton.disabled = false;
submitButton.classList.remove('btn-disabled');
submitButton.style.opacity = '1';
submitButton.style.cursor = 'pointer';
// 移除之前可能添加的点击事件阻止器
submitButton.removeEventListener('click', preventButtonClick);
}
} else {
// 处理未找到比赛的情况
console.error('未找到比赛信息或请求失败:', data.message);
// 更新比赛信息标题
const competitionInfoTitleElement = document.getElementById('competition-info-title');
if (competitionInfoTitleElement) {
competitionInfoTitleElement.textContent = '错误';
}
// 更新比赛公告信息
if (competitionInfoElement) {
competitionInfoElement.textContent = '链接不正确,请输入正确的比赛链接';
competitionInfoElement.style.color = 'red';
competitionInfoElement.classList.add('error');
}
// 禁用登录按钮
if (submitButton) {
submitButton.disabled = true;
submitButton.classList.add('btn-disabled');
submitButton.style.opacity = '0.5';
submitButton.style.cursor = 'not-allowed';
// 添加点击事件阻止器
submitButton.addEventListener('click', preventButtonClick);
}
// 清空比赛时间显示
clearCompetitionTimeDisplay();
}
})
.catch(error => {
console.error('获取比赛信息失败:', error);
// 更新比赛信息标题
const competitionInfoTitleElement = document.getElementById('competition-info-title');
if (competitionInfoTitleElement) {
competitionInfoTitleElement.textContent = '错误';
}
// 更新比赛公告信息
if (competitionInfoElement) {
competitionInfoElement.textContent = '获取比赛信息失败: ' + error.message;
competitionInfoElement.style.color = 'red';
competitionInfoElement.classList.add('error');
}
// 禁用登录按钮
if (submitButton) {
submitButton.disabled = true;
submitButton.classList.add('btn-disabled');
submitButton.style.opacity = '0.5';
submitButton.style.cursor = 'not-allowed';
// 添加点击事件阻止器
submitButton.addEventListener('click', preventButtonClick);
}
// 清空比赛时间显示
clearCompetitionTimeDisplay();
// 重新抛出错误,让调用者可以处理
throw error;
});
}
/**
* 阻止按钮点击事件
* @param {Event} event - 点击事件对象
*/
function preventButtonClick(event) {
event.preventDefault();
event.stopPropagation();
showToast('链接不正确,请输入正确的比赛链接', 'error');
return false;
}
/**
* 清空比赛时间显示
*/
function clearCompetitionTimeDisplay() {
// 清空练习时间
const practiceStartDateElement = document.getElementById('practice-start-date');
const practiceEndDateElement = document.getElementById('practice-end-date');
if (practiceStartDateElement) {
practiceStartDateElement.textContent = '--';
}
if (practiceEndDateElement) {
practiceEndDateElement.textContent = '--';
}
// 清空比赛时间
const competitionDateElement = document.getElementById('competition-date');
const competitionDurationElement = document.getElementById('competition-duration');
if (competitionDateElement) {
competitionDateElement.textContent = '--';
}
if (competitionDurationElement) {
competitionDurationElement.textContent = '--';
}
}
/**
* 更新比赛信息显示
* @param {Object} competition - 比赛信息对象
*/
function updateCompetitionInfo(competition) {
// 将比赛数据保存到全局变量,以便其他函数使用
window.competitionData = competition;
// 将比赛和练习时间存入 localStorage
localStorage.setItem('competition_start_time', competition.competition_start_time);
// 计算比赛结束时间时,先将UTC时间转换为北京时间再加上持续时间
const beijingStartTime = convertUTCToBeijingTime(competition.competition_start_time);
localStorage.setItem('competition_end_time', new Date(beijingStartTime.getTime() + competition.competition_duration * 60000).toISOString());
localStorage.setItem('competition_duration', competition.competition_duration);
localStorage.setItem('practice_start_time', competition.practice_start_time);
localStorage.setItem('practice_end_time', competition.practice_end_time);
localStorage.setItem('competition_name', competition.name);
localStorage.setItem('competition_id', competition.id);
// 存储关卡包信息
if (competition.practice_level_package) {
localStorage.setItem('practice_level_package_id', competition.practice_level_package.id);
localStorage.setItem('practice_level_package_name', competition.practice_level_package.name);
}
if (competition.competition_level_package) {
localStorage.setItem('competition_level_package_id', competition.competition_level_package.id);
localStorage.setItem('competition_level_package_name', competition.competition_level_package.name);
}
// 存储其他比赛相关信息
localStorage.setItem('competition_level', competition.competition_level);
localStorage.setItem('competition_status', competition.status);
localStorage.setItem('competition_is_authorized', competition.is_authorized);
localStorage.setItem('competition_show_competition_time', competition.show_competition_time);
localStorage.setItem('competition_announcement', competition.announcement);
localStorage.setItem('competition_participant_count', competition.participant_count);
localStorage.setItem('competition_max_participants', competition.max_participants);
// 存储背景图片URL和组委会二维码URL(如果存在)
if (competition.background_image_url) {
sessionStorage.setItem('competition_background_image_url', competition.background_image_url);
// 如果没有欢迎图片在显示,则在这里更新背景图
if (!sessionStorage.getItem('competition_welcome_image_url')) {
setBackgroundImage();
}
} else {
// 如果背景图片不存在,则清除背景图片URL
sessionStorage.removeItem('competition_background_image_url');
}
if (competition.committee_qrcode_url) {
sessionStorage.setItem('competition_committee_qrcode_url', competition.committee_qrcode_url);
}
// 存储欢迎图片URL(如果存在)
if (competition.welcome_image_url) {
// 存储欢迎图片,并立即显示欢迎蒙层(如果当前没有显示)
sessionStorage.setItem('competition_welcome_image_url', competition.welcome_image_url);
// 检查当前是否已显示欢迎蒙层,并增加标记防止重复创建
if (!document.querySelector('.welcome-overlay') && !window.welcomeOverlayCreating) {
window.welcomeOverlayCreating = true; // 设置创建标记
showWelcomeImageOverlay(competition.welcome_image_url);
}
} else {
// 如果欢迎图片不存在,则清除欢迎图片URL
sessionStorage.removeItem('competition_welcome_image_url');
}
if (competition.mobile_welcome_image_url) {
sessionStorage.setItem('mobile_welcome_image_url', competition.mobile_welcome_image_url);
} else {
sessionStorage.removeItem('mobile_welcome_image_url');
}
// 更新比赛信息标题
const competitionInfoTitleElement = document.getElementById('competition-info-title');
if (competitionInfoTitleElement) {
competitionInfoTitleElement.textContent = competition.name || '比赛/训练 信息';
}
// 更新比赛公告信息
const announcementElement = document.getElementById('competition-info');
if (announcementElement) {
const announcementText = (competition.announcement || '暂无公告信息').replace(/\n/g, '
');
announcementElement.innerHTML = announcementText;
}
// 更新练习时间 - 使用转换后的北京时间
const practiceStartDateElement = document.getElementById('practice-start-date');
const practiceEndDateElement = document.getElementById('practice-end-date');
if (practiceStartDateElement && competition.practice_start_time) {
const practiceStartDate = convertUTCToBeijingTime(competition.practice_start_time);
practiceStartDateElement.textContent = formatDate(practiceStartDate);
}
if (practiceEndDateElement && competition.practice_end_time) {
const practiceEndDate = convertUTCToBeijingTime(competition.practice_end_time);
practiceEndDateElement.textContent = formatDate(practiceEndDate);
}
// 更新比赛时间 - 使用转换后的北京时间
const competitionDateElement = document.getElementById('competition-date');
const competitionDurationElement = document.getElementById('competition-duration');
const competitionDateTextElement = document.querySelector('.competition-date-text');
// 检查是否允许显示比赛时间
if (competition.show_competition_time === false) {
// 只有特殊地方才屏蔽这个
if(CONFIG.HIDE_COMPETITION_TIME_ID.includes(competitionInfo.id)){
// 如果当前比赛ID在配置的隐藏列表中,则隐藏比赛时间
competitionDateElement.style.display = 'none';
competitionDateTextElement.style.display = 'none';
}
// 如果不显示比赛时间,显示"待定"
if (competitionDateElement) {
competitionDateElement.textContent = '待定';
}
if (competitionDurationElement) {
competitionDurationElement.textContent = '';
}
} else {
// 正常显示比赛时间
if (competitionDateElement && competition.competition_start_time) {
const competitionDate = convertUTCToBeijingTime(competition.competition_start_time);
competitionDateElement.textContent = formatDate(competitionDate);
}
if (competitionDurationElement && competition.competition_duration) {
competitionDurationElement.textContent = `${competition.competition_duration}分钟`;
}
}
// 如果还需要保留原来的详细信息显示,可以继续使用下面的代码
const competitionInfoElement = document.querySelector('.u-text-2');
if (competitionInfoElement) {
// 格式化比赛时间用于详细显示 - 使用转换后的北京时间
const practiceStartTime = convertUTCToBeijingTime(competition.practice_start_time).toLocaleString();
const practiceEndTime = convertUTCToBeijingTime(competition.practice_end_time).toLocaleString();
const competitionStartTime = convertUTCToBeijingTime(competition.competition_start_time).toLocaleString();
// 构建比赛详细信息HTML
const announcementWithBreaks = (competition.announcement || '暂无公告信息').replace(/\n/g, '
');
let infoHTML = `
赛练信息: ${announcementWithBreaks}
赛练状态: ${getStatusText(competition.status)}
赛练区域: ${getLevelText(competition.competition_level)}
`;
localStorage.setItem('competition_announcement', competition.announcement);
// 更新元素内容
competitionInfoElement.innerHTML = infoHTML;
}
// 将所有比赛信息作为一个对象存储,方便其他页面使用
// const competitionFullInfo = {
// id: competition.id,
// name: competition.name,
// announcement: competition.announcement,
// status: competition.status,
// competition_level: competition.competition_level,
// is_authorized: competition.is_authorized,
// practice_start_time: competition.practice_start_time,
// practice_end_time: competition.practice_end_time,
// competition_start_time: competition.competition_start_time,
// competition_duration: competition.competition_duration,
// practice_level_package: competition.practice_level_package,
// competition_level_package: competition.competition_level_package,
// participant_count: competition.participant_count,
// max_participants: competition.max_participants,
// created_at: competition.created_at
// };
// localStorage.setItem('competition_full_info', JSON.stringify(competitionFullInfo));
}
/**
* 格式化日期为YYYY-MM-DD HH:mm格式
* @param {Date} date - 日期对象
* @returns {string} 格式化后的日期字符串
*/
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}`;
}
/**
* 获取比赛状态的中文描述
* @param {string} status - 比赛状态
* @returns {string} 状态的中文描述
*/
function getStatusText(status) {
const statusMap = {
'draft': '草稿',
'published': '已发布',
'ongoing': '进行中',
'ended': '已结束',
'cancelled': '已取消'
};
return statusMap[status] || status;
}
/**
* 获取比赛级别的中文描述
* @param {string} level - 比赛级别
* @returns {string} 级别的中文描述
*/
function getLevelText(level) {
const levelMap = {
'national': '全国级',
'provincial': '全省',
'city': '市级',
'district': '区级',
'school': '校级'
};
return levelMap[level] || level;
}
/**
* 切换密码可见性
*/
function togglePasswordVisibility() {
const passwordInput = document.getElementById('email-b064');
const eyeSlash = document.getElementById('eye-slash');
if (passwordInput.type === 'password') {
passwordInput.type = 'text';
eyeSlash.style.display = 'none';
} else {
passwordInput.type = 'password';
eyeSlash.style.display = 'block';
}
}
/**
* 调用登录API验证用户
* @param {string} username - 用户名
* @param {string} password - 密码
* @param {string} platformType - 平台类型 (practice/competition)
* @param {string} codetype - 编程语言类型 (可选)
*/
function loginUser(username, password, platformType, codetype = null) {
// 检查是否处于锁定状态
const currentTime = new Date().getTime();
if (currentTime < loginLockUntil) {
const remainingSeconds = Math.ceil((loginLockUntil - currentTime) / 1000);
showToast(`登录失败次数过多,请在${remainingSeconds}秒后再试`, 'error');
return;
}
// 获取提交按钮
const submitButton = document.querySelector('.u-btn-submit');
// 禁用按钮并添加加载状态
if (submitButton) {
submitButton.classList.add('btn-loading');
submitButton.setAttribute('disabled', 'disabled');
submitButton.style.pointerEvents = 'none';
}
// 显示加载状态
showToast('正在验证账号信息...', 'info');
// 构建请求数据
const requestData = {
username: username,
password: password,
tryuse: true // 允许试用
};
// 发送POST请求到登录API
fetch(`${CONFIG.DataServerBaseUrl}/api/login/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
console.log('登录响应:', data);
if (data.success) {
// 登录成功,重置失败计数
loginFailCount = 0;
loginLockUntil = 0;
// 更新localStorage
localStorage.removeItem('loginFailCount');
localStorage.removeItem('loginLockUntil');
// 登录成功
showToast('登录成功', 'success');
// 获取比赛ID
const competitionId = getUrlParameter('competition_id');
localStorage.setItem('competition_id', competitionId);
// 保存用户信息到localStorage
localStorage.setItem('access_token', data.access);
localStorage.setItem('player_id', data.player_id);
localStorage.setItem('username', username);
localStorage.setItem('full_name', data.full_name);
localStorage.setItem('qrid', data.qrid);
localStorage.setItem('permission', data.permission);
localStorage.setItem('cityid', data.cityid);
localStorage.setItem('districtid', data.districtid);
localStorage.setItem('institutionid', data.institutionid);
// localStorage.setItem('competitionId', competitionId);
localStorage.setItem('platformType', platformType);
//TODO 将比赛练习时间放到localStorage中
// 保存当前页面URL到localStorage
// sessionStorage.setItem('lastpage', window.location.href);
// 存储当前页面的相对路径和参数
const currentPath = window.location.pathname + window.location.search;
sessionStorage.setItem('lastpage', currentPath.substring(1)); // 去掉开头的斜杠
if (competitionId) {
// 显示正在核验赛练信息的提示
showToast('正在核验赛练信息...', 'info');
// 检查用户是否在比赛中
checkUserInCompetition(data.access, competitionId, platformType, codetype);
} else {
// 没有比赛ID,直接根据平台类型跳转
// redirectBasedOnPlatform(platformType);
showToast('此比赛链接错误', 'error');
}
} else {
// 登录失败,增加失败计数
loginFailCount++;
// 更新localStorage
localStorage.setItem('loginFailCount', loginFailCount);
// 检查是否达到最大失败次数
if (loginFailCount >= 5) {
// 设置锁定时间为当前时间 + 1分钟
loginLockUntil = new Date().getTime() + (60 * 1000);
localStorage.setItem('loginLockUntil', loginLockUntil);
showToast('登录失败次数过多,请在1分钟后再试', 'error');
} else {
// 登录失败,显示错误信息
let errorMessage = data.message || '登录失败';
// 根据错误ID显示不同的错误信息
switch (data.id) {
case 2:
errorMessage = `用户名或密码错误
(第${loginFailCount}次失败,5次后将暂时锁定)`;
break;
case 3:
errorMessage = '您没有权限登录或权限已过期';
break;
case 4:
errorMessage = '无效的请求方式';
break;
case 5:
errorMessage = '无效的请求格式';
break;
case 6:
errorMessage = '用户名和密码不能为空';
break;
default:
errorMessage = data.message || '登录失败';
}
showToast(errorMessage, 'error');
}
// 恢复按钮状态
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
}
})
.catch(error => {
console.error('登录请求失败:', error);
// 显示错误信息
showToast('网络错误,请稍后重试', 'error');
// 恢复按钮状态
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
});
}
/**
* 检查用户是否在比赛中
* @param {string} token - 用户访问令牌
* @param {string|number} competitionId - 比赛ID
* @param {string} platformType - 平台类型
* @param {string} codetype - 编程语言类型 (可选)
*/
function checkUserInCompetition(token, competitionId, platformType, codetype = null) {
// 如果是活动比赛,先检查绑定状态
const competitionIdInt = parseInt(competitionId, 10);
const isSSNCompetition = Array.isArray(CONFIG.SSN_COMPETITION_ID)
? CONFIG.SSN_COMPETITION_ID.includes(competitionIdInt)
: competitionIdInt === CONFIG.SSN_COMPETITION_ID;
if (isSSNCompetition) {
//调用第三方接口绑定数据
showToast('正在检查活动报名信息...', 'info');
// 先查询用户是否已经绑定
checkUserSSNBinding();
return; // 停止后续流程,等待绑定检查完成
}
// 构建请求数据
const requestData = {
competition_id: parseInt(competitionId, 10),
page: 1,
page_size: 10,
username: localStorage.getItem('username')
};
// 发送请求检查用户是否在比赛中
fetch(`${CONFIG.DataServerBaseUrl}/api/competition/query_competition_participants/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
console.log('比赛参与者查询响应:', data);
// 检查比赛是否需要授权
const competition = window.competitionData;
const isAuthorized = competition && competition.is_authorized;
if (data.success && data.data && data.data.participants && data.data.participants.length > 0) {
// 用户在比赛中,跳转到比赛页面
localStorage.setItem("codetype", parseInt(data.data.participants[0].codetype, 10))
showToast('验证成功,正在进入赛练...', 'success');
window.location.href = 'competition_sum.html?competition_id=' + competitionId;
} else if (isAuthorized) {
// 比赛需要授权但用户未被授权
showToast('您尚无法参与此活动,请联系管理员', 'error');
// 恢复按钮状态
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
} else {
// 用户不在比赛中,根据 codetype 或 type 参数处理
if (codetype) {
// 已经有 codetype,直接加入比赛
joinCompetition(competitionId, codetype);
} else {
// 没有 codetype,检查 URL 中的 type 参数
const types = getTypeListFromUrl();
if (types.length === 1) {
// 只有一个类型,直接加入比赛
joinCompetition(competitionId, types[0]);
} else {
// 多个类型,弹出选择模态框
showCodetypeModal(types);
showToast('请选择参赛类型', 'info');
// 恢复按钮状态
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
}
}
}
})
.catch(error => {
console.error('检查用户是否在比赛中失败:', error);
showToast('验证赛练信息失败,请稍后重试', 'error');
// 恢复按钮状态
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
});
}
/**
* 根据平台类型重定向到相应页面
* @param {string} platformType - 平台类型
*/
function redirectBasedOnPlatform(platformType) {
if (platformType === 'practice') {
// 跳转到训练平台
window.location.href = 'practice.html';
} else if (platformType === 'competition') {
// 跳转到比赛平台
window.location.href = 'exam.html';
}
}
/**
* 显示Toast消息
* @param {string} message - 消息内容
* @param {string} type - 消息类型 (success/error/info)
* @param {number} duration - 显示时间(毫秒),默认3000ms
*/
function showToast(message, type = 'info', duration = 3000) {
// 创建 Toast 容器(如果不存在)
let toastContainer = document.getElementById('toast-container');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.id = 'toast-container';
toastContainer.style.position = 'fixed';
toastContainer.style.top = '20px';
toastContainer.style.left = '50%';
toastContainer.style.transform = 'translateX(-50%)';
toastContainer.style.zIndex = '9999';
document.body.appendChild(toastContainer);
}
// 创建新的 Toast 元素
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.innerHTML = message; // 使用innerHTML而不是textContent以支持HTML标签
// 添加到容器
toastContainer.appendChild(toast);
// 显示 Toast(添加延迟以确保 CSS 过渡效果正常工作)
setTimeout(() => {
toast.style.opacity = '0.9';
toast.style.transform = 'translateY(0)';
}, 10);
// 指定时间后隐藏 Toast
setTimeout(() => {
toast.classList.add('fade-out');
// 动画完成后移除元素
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, 500);
}, duration);
}
/**
* 加入比赛
* @param {string|number} competitionId - 比赛ID
* @param {string|number} codeType - 编码类型
*/
function joinCompetition(competitionId, codeType) {
// 获取提交按钮
const submitButton = document.querySelector('.u-btn-step');
// 禁用按钮并添加加载状态
if (submitButton) {
submitButton.classList.add('btn-loading');
submitButton.setAttribute('disabled', 'disabled');
submitButton.style.pointerEvents = 'none';
}
// 显示加载状态
showToast('正在加入比赛...', 'info');
// 获取访问令牌
const token = localStorage.getItem('access_token');
// 如果是活动比赛,需要更新活动编程语言
const competitionIdInt = parseInt(competitionId, 10);
const isSSNCompetition = Array.isArray(CONFIG.SSN_COMPETITION_ID)
? CONFIG.SSN_COMPETITION_ID.includes(competitionIdInt)
: competitionIdInt === CONFIG.SSN_COMPETITION_ID;
console.error(isSSNCompetition)
if (isSSNCompetition) {
// 等待更新完成,如果失败则不继续执行
updateSSNCodingLanguage(parseInt(codeType, 10), token)
.then(() => {
// 更新成功,继续执行加入比赛流程
executeJoinCompetition(competitionId, codeType, token);
})
.catch((error) => {
// 更新失败,结束流程
console.error('活动编程语言更新失败,终止加入比赛流程:', error);
// 恢复按钮状态
const submitButton = document.querySelector('.u-btn-step');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
});
return; // 提前返回,不执行后续的加入比赛流程
}
// 如果不是比赛ID为11,直接执行加入比赛流程
executeJoinCompetition(competitionId, codeType, token);
}
/**
* 执行加入比赛的核心逻辑
* @param {string|number} competitionId - 比赛ID
* @param {string|number} codeType - 编码类型
* @param {string} token - 访问令牌
*/
function executeJoinCompetition(competitionId, codeType, token) {
// 构建请求数据
const requestData = {
competition_id: parseInt(competitionId, 10),
has_practice_permission: true,
codetype: parseInt(codeType, 10)
};
// 发送请求加入比赛
fetch(`${CONFIG.DataServerBaseUrl}/api/competition/join_competition/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
console.error('加入比赛响应:', data);
// 关闭模态框
const modal = document.getElementById('carousel_cd2f');
if (modal) {
modal.style.display = 'none';
}
if (data.success) {
// 加入比赛成功
showToast('加入比赛成功', 'success');
// 保存比赛信息到localStorage
if (data.data) {
localStorage.setItem('competition_id', data.data.competition.id);
localStorage.setItem('competition_name', data.data.competition.name);
localStorage.setItem('codetype', data.data.codetype);
localStorage.setItem('registration_time', data.data.registration_time);
}
// 延迟跳转到比赛页面
setTimeout(() => {
// 关闭模态框
closeModal();
window.location.href = 'competition_sum.html?competition_id=' + competitionId;
}, 1000);
} else {
// 加入比赛失败
showToast(data.message || '加入比赛失败', 'error');
// 恢复按钮状态
const submitButton = document.querySelector('.u-btn-step');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
}
})
.catch(error => {
console.error('加入比赛请求失败:', error);
showToast('网络错误,请稍后重试', 'error');
// 恢复按钮状态
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
});
}
/**
* 关闭模态框
*/
function closeModal() {
// 尝试找到模态框的关闭按钮并点击它
const closeButton = document.getElementById('close-codetype-modal-button');
closeButton.click();
}
/**
* 加载省份数据
*/
function loadProvinces() {
fetch(`${CONFIG.DataServerBaseUrl}/api/provinces/`)
.then(response => response.json())
.then(data => {
const provinceSelect = document.getElementById('select-c2b7');
if (provinceSelect) {
// 清空现有选项,保留默认选项
provinceSelect.innerHTML = '';
// 添加新的省份选项
data.forEach(province => {
const option = document.createElement('option');
option.value = province.id;
option.textContent = province.name;
provinceSelect.appendChild(option);
});
}
})
.catch(error => {
console.error('加载省份数据失败:', error);
showToast('加载省份数据失败', 'error');
});
}
/**
* 加载城市数据
* @param {number} provinceId - 省份ID
*/
function loadCities(provinceId) {
fetch(`${CONFIG.DataServerBaseUrl}/api/cities/${provinceId}/`)
.then(response => response.json())
.then(data => {
const citySelect = document.getElementById('select-36ac');
if (citySelect) {
// 清空现有选项,保留默认选项
citySelect.innerHTML = '';
// 添加新的城市选项
data.forEach(city => {
const option = document.createElement('option');
option.value = city.id;
option.textContent = city.name;
citySelect.appendChild(option);
});
// 清空区县选择框
const districtSelect = document.getElementById('select-4860');
if (districtSelect) {
districtSelect.innerHTML = '';
}
}
})
.catch(error => {
console.error('加载城市数据失败:', error);
showToast('加载城市数据失败', 'error');
});
}
/**
* 加载区县数据
* @param {number} cityId - 城市ID
*/
function loadDistricts(cityId) {
fetch(`${CONFIG.DataServerBaseUrl}/api/districts/${cityId}/`)
.then(response => response.json())
.then(data => {
const districtSelect = document.getElementById('select-4860');
if (districtSelect) {
// 清空现有选项,保留默认选项
districtSelect.innerHTML = '';
// 添加新的区县选项
data.forEach(district => {
const option = document.createElement('option');
option.value = district.id;
option.textContent = district.name;
districtSelect.appendChild(option);
});
}
})
.catch(error => {
console.error('加载区县数据失败:', error);
showToast('加载区县数据失败', 'error');
});
}
/**
* 根据区县ID加载学校数据
* @param {string} districtId - 区县ID
*/
async function loadSchoolsByDistrict(districtId) {
try {
const response = await fetch(`${CONFIG.DataServerBaseUrl}/api/institutions/by-district-without-permission/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify({
district_id: districtId,
page: 1,
page_size: 100 // 获取足够多的学校
})
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const result = await response.json();
console.log('Competition schools response:', result);
if (!result.success) {
throw new Error(result.message || '获取学校列表失败');
}
// 使用Competition页面的学校数据设置函数
if (result.data && result.data.institutions && result.data.institutions.length > 0) {
if (typeof window.populateSchoolsWithSearchComp === 'function') {
window.populateSchoolsWithSearchComp(result.data.institutions);
}
} else {
// 如果没有学校数据,清空搜索数据
if (typeof window.populateSchoolsWithSearchComp === 'function') {
window.populateSchoolsWithSearchComp([]);
}
}
console.log(`Competition page loaded ${result.data.institutions ? result.data.institutions.length : 0} schools.`);
} catch (error) {
console.error('Error loading schools for competition:', error);
showToast(`无法加载学校数据,请稍后再试。${error.message}`, 'error');
}
}
/**
* 加载年级数据
*/
function loadGrades() {
fetch(`${CONFIG.DataServerBaseUrl}/api/grades/`)
.then(response => response.json())
.then(data => {
const gradeSelect = document.getElementById('select-2088');
if (gradeSelect) {
// 清空现有选项,保留默认选项
gradeSelect.innerHTML = '';
// 添加新的年级选项
data.forEach(grade => {
const option = document.createElement('option');
option.value = grade.id;
option.textContent = grade.name;
gradeSelect.appendChild(option);
});
}
})
.catch(error => {
console.error('加载年级数据失败:', error);
showToast('加载年级数据失败', 'error');
});
}
/**
* 搜索学校 - 已废弃,现在使用弹窗搜索
* @param {string} keyword - 搜索关键词
* @param {HTMLElement} container - 显示结果的容器
*/
/*
function searchSchools(keyword, container) {
fetch(`${CONFIG.DataServerBaseUrl}/api/institutions/search/${encodeURIComponent(keyword)}/`)
.then(response => response.json())
.then(data => {
// 清空容器
container.innerHTML = '';
if (data && data.length > 0) {
// 创建学校列表
const ul = document.createElement('ul');
ul.className = 'school-list';
// 添加学校选项
data.forEach(school => {
const li = document.createElement('li');
li.className = 'school-item';
li.textContent = school.name;
li.dataset.id = school.id;
// 添加点击事件
li.addEventListener('click', function() {
const schoolInput = document.getElementById('text-21f2');
if (schoolInput) {
schoolInput.value = school.name;
// 存储学校ID,用于提交表单
schoolInput.dataset.schoolId = school.id;
// 隐藏候选列表
container.style.display = 'none';
}
});
ul.appendChild(li);
});
// 添加到容器
container.appendChild(ul);
container.style.display = 'block';
} else {
container.style.display = 'none';
}
})
.catch(error => {
console.error('搜索学校失败:', error);
container.style.display = 'none';
});
}
*/
/**
* 验证注册表单
* @returns {boolean} 表单是否验证通过
*/
function validateRegisterForm() {
// 获取表单字段
const username = document.getElementById('phone-23d9').value.trim();
const password = document.getElementById('text-b46f').value.trim();
const fullName = document.getElementById('text-bf16').value.trim();
const phoneNumber = document.getElementById('text-8d13').value.trim();
const email = document.getElementById('email-594e').value.trim();
const province = document.getElementById('select-c2b7').value;
const city = document.getElementById('select-36ac').value;
const district = document.getElementById('select-4860').value;
// 获取选中的学校ID和名称
const selectedSchoolIdInputComp = document.getElementById('selected-school-id-comp');
const schoolSelectComp = document.getElementById('school-select-comp');
const selectedSchoolId = selectedSchoolIdInputComp ? selectedSchoolIdInputComp.value : '-1';
const school = schoolSelectComp && schoolSelectComp.options[0] ? schoolSelectComp.options[0].textContent : '';
const grade = document.getElementById('select-2088').value;
// 验证用户名
if (!username) {
showToast('请输入用户名', 'error');
return false;
}
if (username.length > 20) {
showToast('用户名不能超过20个字符', 'error');
return false;
}
// 用户名只能包含英文字母和数字,不允许中文和特殊符号
if (!/^[a-zA-Z0-9]+$/.test(username)) {
showToast('用户名只能包含英文字母和数字', 'error');
return false;
}
// 验证密码
if (!password) {
showToast('请输入密码', 'error');
return false;
}
if (password.length < 6) {
showToast('密码长度不能少于6个字符', 'error');
return false;
}
// 验证姓名
if (!fullName) {
showToast('请输入姓名', 'error');
return false;
}
if (fullName.length > 20) {
showToast('姓名不能超过20个字符', 'error');
return false;
}
// 姓名只能包含中英文字符
if (!/^[\u4e00-\u9fa5a-zA-Z]+$/.test(fullName)) {
showToast('姓名只能包含中英文字符', 'error');
return false;
}
// 验证电话号码
if (!phoneNumber) {
showToast('请输入电话号码', 'error');
return false;
}
// 电话号码必须是11位数字
if (!/^\d{11}$/.test(phoneNumber)) {
showToast('电话号码必须是11位数字', 'error');
return false;
}
// 验证邮箱
if (!email) {
showToast('请输入邮箱地址', 'error');
return false;
}
// 验证邮箱格式
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
showToast('请输入有效的邮箱地址', 'error');
return false;
}
// 验证省市区
if (province === '省') {
showToast('请选择省份', 'error');
return false;
}
if (city === '市') {
showToast('请选择城市', 'error');
return false;
}
if (district === '区/县') {
showToast('请选择区/县', 'error');
return false;
}
// 验证学校
if (!school || school === '请选择学校') {
showToast('请选择学校', 'error');
return false;
}
// 验证年级
if (grade === '年级') {
showToast('请选择年级', 'error');
return false;
}
// 所有验证通过
return true;
}
/**
* 设置密码输入框的显示/隐藏功能
*/
function setupPasswordToggle() {
// 登录表单密码框已经在HTML中设置好了
// 为注册表单的密码框添加显示/隐藏功能
const registerPasswordInput = document.getElementById('text-b46f');
if (registerPasswordInput) {
// 创建密码容器
const passwordContainer = document.createElement('div');
passwordContainer.className = 'password-container';
// 将密码输入框放入容器中
registerPasswordInput.parentNode.insertBefore(passwordContainer, registerPasswordInput);
passwordContainer.appendChild(registerPasswordInput);
// 确保密码框文本居中
registerPasswordInput.style.textAlign = 'center';
registerPasswordInput.style.paddingLeft = '40px';
registerPasswordInput.style.paddingRight = '40px';
registerPasswordInput.style.boxSizing = 'border-box';
// 创建切换按钮
const toggleButton = document.createElement('span');
toggleButton.className = 'password-toggle';
toggleButton.innerHTML = `
`;
// 添加点击事件
toggleButton.addEventListener('click', function () {
toggleRegisterPasswordVisibility();
});
// 将切换按钮添加到容器中
passwordContainer.appendChild(toggleButton);
}
}
/**
* 切换注册表单密码的可见性
*/
function toggleRegisterPasswordVisibility() {
const passwordInput = document.getElementById('text-b46f');
const eyeSlash = document.getElementById('register-eye-slash');
if (passwordInput && eyeSlash) {
if (passwordInput.type === 'password') {
passwordInput.type = 'text';
eyeSlash.style.display = 'block';
} else {
passwordInput.type = 'password';
eyeSlash.style.display = 'none';
}
}
}
/**
* 设置自定义平台选择下拉菜单
*/
function setupCustomPlatformSelect() {
const platformSelect = document.getElementById('select-58f1');
if (!platformSelect) return;
// 获取原始选项
const options = Array.from(platformSelect.options).map(option => ({
value: option.value,
text: option.textContent,
disabled: option.disabled
}));
// 创建自定义下拉菜单容器
const customSelectContainer = document.createElement('div');
customSelectContainer.className = 'custom-select-container';
// 创建显示选中值的元素
const selectedDisplay = document.createElement('div');
selectedDisplay.className = 'custom-select-selected';
selectedDisplay.textContent = options.find(opt => opt.value === platformSelect.value)?.text || '请选择平台';
selectedDisplay.setAttribute('data-value', platformSelect.value);
// 创建下拉选项容器
const optionsContainer = document.createElement('div');
optionsContainer.className = 'custom-select-options';
optionsContainer.style.display = 'none';
// 添加选项
options.forEach(option => {
const optionElement = document.createElement('div');
optionElement.className = 'custom-select-option';
optionElement.textContent = option.text;
optionElement.setAttribute('data-value', option.value);
if (option.disabled) {
optionElement.classList.add('disabled');
} else {
// 为可选选项添加点击事件
optionElement.addEventListener('click', function () {
const value = this.getAttribute('data-value');
platformSelect.value = value;
selectedDisplay.textContent = this.textContent;
selectedDisplay.setAttribute('data-value', value);
optionsContainer.style.display = 'none';
// 触发原始select的change事件
const event = new Event('change', { bubbles: true });
platformSelect.dispatchEvent(event);
});
}
optionsContainer.appendChild(optionElement);
});
// 点击显示/隐藏下拉选项
selectedDisplay.addEventListener('click', function (event) {
event.stopPropagation();
const isVisible = optionsContainer.style.display === 'block';
optionsContainer.style.display = isVisible ? 'none' : 'block';
});
// 点击页面其他地方关闭下拉菜单
document.addEventListener('click', function () {
optionsContainer.style.display = 'none';
});
// 组装自定义下拉菜单
customSelectContainer.appendChild(selectedDisplay);
customSelectContainer.appendChild(optionsContainer);
// 隐藏原始select元素及其父容器中的SVG箭头
platformSelect.style.display = 'none';
// 查找并隐藏原生下拉箭头
const parentWrapper = platformSelect.closest('.u-form-select-wrapper');
if (parentWrapper) {
const svgArrow = parentWrapper.querySelector('.u-caret');
if (svgArrow) {
svgArrow.style.display = 'none';
}
}
// 将自定义下拉菜单添加到DOM
if (parentWrapper) {
parentWrapper.appendChild(customSelectContainer);
} else {
platformSelect.parentNode.insertBefore(customSelectContainer, platformSelect.nextSibling);
}
// 监听原始select的变化,更新自定义下拉菜单
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
updateCustomSelectOptions();
}
});
});
// 监听原始select的子元素变化
observer.observe(platformSelect, {
attributes: true,
childList: true,
subtree: true
});
// 更新自定义下拉菜单选项
function updateCustomSelectOptions() {
// 清空现有选项
optionsContainer.innerHTML = '';
// 获取最新选项
const updatedOptions = Array.from(platformSelect.options).map(option => ({
value: option.value,
text: option.textContent,
disabled: option.disabled
}));
// 重新添加选项
updatedOptions.forEach(option => {
const optionElement = document.createElement('div');
optionElement.className = 'custom-select-option';
optionElement.textContent = option.text;
optionElement.setAttribute('data-value', option.value);
if (option.disabled) {
optionElement.classList.add('disabled');
} else {
// 为可选选项添加点击事件
optionElement.addEventListener('click', function () {
const value = this.getAttribute('data-value');
platformSelect.value = value;
selectedDisplay.textContent = this.textContent;
selectedDisplay.setAttribute('data-value', value);
optionsContainer.style.display = 'none';
// 触发原始select的change事件
const event = new Event('change', { bubbles: true });
platformSelect.dispatchEvent(event);
});
}
optionsContainer.appendChild(optionElement);
});
// 更新选中显示
const selectedOption = updatedOptions.find(opt => opt.value === platformSelect.value);
if (selectedOption) {
selectedDisplay.textContent = selectedOption.text;
selectedDisplay.setAttribute('data-value', selectedOption.value);
}
}
console.log('自定义下拉菜单设置完成,准备显示引导提示');
// 在菜单设置完成后显示引导提示,确保元素已存在
// 稍微延迟确保DOM已更新
setTimeout(() => showPlatformGuide(), 500);
}
/**
* 更新平台选择下拉框状态
*/
function updatePlatformSelectStatus() {
// 如果比赛信息和服务器时间都已获取
if (competitionInfo && serverTime) {
// 判断训练时间模块的显示和隐藏
const practiceEndTimestamp = convertUTCToBeijingTime(competitionInfo.practice_end_time).getTime() / 1000
if(serverTime.timestamp > practiceEndTimestamp){
const practiceTimeSection = document.querySelector('.meta-line');
practiceTimeSection.style.display = 'none';
}
const platformSelect = document.getElementById('select-58f1');
if (!platformSelect) return;
const practiceOption = platformSelect.querySelector('option[value="practice"]');
const competitionOption = platformSelect.querySelector('option[value="competition"]');
if (!practiceOption || !competitionOption) return;
// 获取当前时间戳(秒)
const currentTimestamp = serverTime.timestamp;
// 获取练习时间范围 - 使用转换后的北京时间
const practiceStartTime = convertUTCToBeijingTime(competitionInfo.practice_start_time).getTime() / 1000;
const practiceEndTime = convertUTCToBeijingTime(competitionInfo.practice_end_time).getTime() / 1000;
// 获取比赛日期(不考虑具体时间) - 使用转换后的北京时间
const competitionDate = convertUTCToBeijingTime(competitionInfo.competition_start_time);
competitionDate.setHours(0, 0, 0, 0); // 设置为当天的开始时间
const competitionDateStart = competitionDate.getTime() / 1000;
// 比赛日结束时间(当天的23:59:59)
const competitionDateEnd = competitionDateStart + (24 * 60 * 60) - 1;
console.log('当前时间戳:', currentTimestamp, new Date(currentTimestamp * 1000).toLocaleString());
console.log('练习开始时间:', practiceStartTime, new Date(practiceStartTime * 1000).toLocaleString());
console.log('练习结束时间:', practiceEndTime, new Date(practiceEndTime * 1000).toLocaleString());
console.log('比赛日开始时间:', competitionDateStart, new Date(competitionDateStart * 1000).toLocaleString());
console.log('比赛日结束时间:', competitionDateEnd, new Date(competitionDateEnd * 1000).toLocaleString());
// 检查是否在练习时间内
const isPracticeTime = currentTimestamp >= practiceStartTime && currentTimestamp <= practiceEndTime;
// 检查是否在比赛日当天
const isCompetitionDay = currentTimestamp >= competitionDateStart && currentTimestamp <= competitionDateEnd;
console.log('是否在练习时间内:', isPracticeTime);
console.log('是否在比赛日当天:', isCompetitionDay);
// 更新自定义下拉菜单
const customSelectContainer = document.querySelector('.custom-select-container');
if (customSelectContainer) {
const optionsContainer = customSelectContainer.querySelector('.custom-select-options');
if (optionsContainer) {
const practiceCustomOption = optionsContainer.querySelector('[data-value="practice"]');
const competitionCustomOption = optionsContainer.querySelector('[data-value="competition"]');
if (practiceCustomOption) {
if (!isPracticeTime) {
practiceCustomOption.classList.add('disabled');
practiceCustomOption.textContent = "训练平台 (不在训练时间内)";
} else {
practiceCustomOption.classList.remove('disabled');
practiceCustomOption.textContent = "训练平台";
}
}
if (competitionCustomOption) {
if (!isCompetitionDay) {
competitionCustomOption.classList.add('disabled');
competitionCustomOption.textContent = "比赛平台 (不在比赛日期)";
} else {
competitionCustomOption.classList.remove('disabled');
competitionCustomOption.textContent = "比赛平台";
}
}
}
}
}
}
/**
* 验证当前时间是否允许选择指定的平台
* @param {string} platformType - 平台类型 ('practice' 或 'competition')
* @returns {boolean} 是否允许选择
*/
function validateTimeForPlatform(platformType) {
// 如果比赛信息或服务器时间未获取,允许操作
if (!competitionInfo || !serverTime) return true;
// 获取当前时间戳(秒)
const currentTimestamp = serverTime.timestamp;
if (platformType === 'practice') {
// 获取练习时间范围 - 使用转换后的北京时间
const practiceStartTime = convertUTCToBeijingTime(competitionInfo.practice_start_time).getTime() / 1000;
const practiceEndTime = convertUTCToBeijingTime(competitionInfo.practice_end_time).getTime() / 1000;
// 检查是否在练习时间内
if (currentTimestamp < practiceStartTime) {
showToast('训练尚未开始,请在训练开始时间后再试', 'error');
return false;
} else if (currentTimestamp > practiceEndTime) {
showToast('训练已结束,无法进入训练平台', 'error');
return false;
}
} else if (platformType === 'competition') {
// 获取比赛日期(不考虑具体时间) - 使用转换后的北京时间
const competitionDate = convertUTCToBeijingTime(competitionInfo.competition_start_time);
let competitionDateStart, competitionDateEnd;
// 检查是否为模拟比赛
if (CONFIG.SIMULATE_COMPETITION_ID.includes(competitionInfo.id)) {
// 模拟比赛:按具体开始时间和时长计算
competitionDateStart = competitionDate.getTime() / 1000;
competitionDateEnd = competitionDateStart + Number(competitionInfo.competition_duration) * 60;
} else {
// 正式比赛:当天全天有效 (00:00:00 - 23:59:59)
competitionDate.setHours(0, 0, 0, 0);
competitionDateStart = competitionDate.getTime() / 1000;
competitionDateEnd = competitionDateStart + (24 * 60 * 60) - 1;
}
// 检查是否在比赛日当天
if (currentTimestamp < competitionDateStart) {
showToast('比赛尚未开始,请在比赛日期再试', 'error');
return false;
} else if (currentTimestamp > competitionDateEnd) {
showToast('比赛已结束,无法进入比赛平台', 'error');
return false;
}
}
return true;
}
/**
* 检查用户是否已经绑定活动信息
*/
function checkUserSSNBinding() {
// 获取用户信息
const playerId = localStorage.getItem('player_id');
const token = localStorage.getItem('access_token');
if (!playerId || !token) {
showToast('用户信息获取失败', 'error');
// 恢复登录按钮状态
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
return;
}
// 构建请求数据
const requestData = {
user_id: playerId
};
// 发送请求查询用户绑定状态
fetch(`${CONFIG.DataServerBaseUrl}/api/v1/third-party/get-user-ssn/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
console.log('用户SSN绑定状态查询响应:', data);
if (data.success) {
// 检查是否有绑定记录
if (data.data && data.data.ssn_records && data.data.ssn_records.length > 0) {
// 用户已经绑定过,存储绑定信息并继续流程
showToast('检测到已绑定活动报名信息', 'success');
// 存储绑定信息(使用最新的一条记录)
const latestRecord = data.data.ssn_records[0];
localStorage.setItem('ssn_register_info', JSON.stringify(latestRecord));
// 存储SSN用户信息到sessionStorage,用于在competition_sum页面显示
const ssnUserInfo = {
ssn_register_id: latestRecord.ssn_register_id,
name: latestRecord.name,
id_card: latestRecord.id_card,
org: latestRecord.org
};
sessionStorage.setItem('ssn_user_info', JSON.stringify(ssnUserInfo));
// 延迟后继续原来的流程
setTimeout(() => {
continueCheckUserInCompetition();
}, 1000);
} else {
// 用户尚未绑定,显示绑定弹窗
showToast('请绑定活动报名信息', 'info');
showSSNBindingModal();
}
} else {
// 查询失败,显示绑定弹窗(保险起见)
showToast('无法检查绑定状态,请手动绑定', 'info');
showSSNBindingModal();
}
})
.catch(error => {
console.error('查询用户SSN绑定状态失败:', error);
// 网络错误,显示绑定弹窗(保险起见)
showToast('网络错误,请手动绑定活动报名信息', 'info');
showSSNBindingModal();
});
}
/**
* 显示活动身份证绑定弹窗
*/
function showSSNBindingModal() {
// 创建弹窗蒙层
const overlay = document.createElement('div');
overlay.id = 'ssn-binding-overlay';
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 99999;
backdrop-filter: blur(5px);
`;
// 创建弹窗内容
const modal = document.createElement('div');
modal.style.cssText = `
background: #FFFFFF;
padding: 32px;
border-radius: 10px;
box-shadow: 0px 34px 44px -20px rgba(185, 206, 234, 0.25);
text-align: center;
max-width: 450px;
width: 90%;
position: relative;
display: flex;
flex-direction: column;
gap: 24px;
`;
modal.innerHTML = `
`;
overlay.appendChild(modal);
document.body.appendChild(overlay);
// 获取元素
const idCardInput = document.getElementById('ssn-id-card');
const bindBtn = document.getElementById('ssn-bind-btn');
const cancelBtn = document.getElementById('ssn-cancel-btn');
const closeBtn = document.getElementById('ssn-close-btn');
// 输入框聚焦效果
idCardInput.addEventListener('focus', function () {
this.style.borderColor = '#5380ea';
this.style.background = '#ffffff';
this.style.boxShadow = '0 0 0 3px rgba(83, 128, 234, 0.1)';
});
idCardInput.addEventListener('blur', function () {
this.style.borderColor = '#e2e8f0';
this.style.background = '#f5f6f9';
this.style.boxShadow = 'none';
});
// 关闭按钮事件
closeBtn.addEventListener('click', function () {
document.body.removeChild(overlay);
});
// 身份证号码格式验证
function validateIdCard(idCard) {
const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
return idCardRegex.test(idCard);
}
// 输入框实时验证
idCardInput.addEventListener('input', function () {
const idCard = this.value.trim();
if (idCard.length === 18) {
if (validateIdCard(idCard)) {
bindBtn.disabled = false;
bindBtn.style.opacity = '1';
bindBtn.style.cursor = 'pointer';
} else {
bindBtn.disabled = true;
bindBtn.style.opacity = '0.5';
bindBtn.style.cursor = 'not-allowed';
}
} else {
bindBtn.disabled = true;
bindBtn.style.opacity = '0.5';
bindBtn.style.cursor = 'not-allowed';
}
});
// 为绑定按钮添加悬停效果
bindBtn.addEventListener('mouseenter', function () {
this.style.transform = 'scale(1.02)';
this.style.boxShadow = '7px 7px 25px 0 rgba(0,0,0,0.5)';
});
bindBtn.addEventListener('mouseleave', function () {
this.style.transform = 'scale(1)';
this.style.boxShadow = '5px 5px 20px 0 rgba(0,0,0,0.4)';
});
// 为取消按钮添加悬停效果
cancelBtn.addEventListener('mouseenter', function () {
this.style.transform = 'scale(1.02)';
this.style.boxShadow = '7px 7px 25px 0 rgba(0,0,0,0.5)';
});
cancelBtn.addEventListener('mouseleave', function () {
this.style.transform = 'scale(1)';
this.style.boxShadow = '5px 5px 20px 0 rgba(0,0,0,0.4)';
});
// 绑定按钮点击事件
bindBtn.addEventListener('click', function () {
const idCard = idCardInput.value.trim();
if (!validateIdCard(idCard)) {
showToast('请输入有效的身份证号码', 'error');
return;
}
// 调用绑定接口
bindSSNRegisterInfo(idCard, overlay);
});
// 取消按钮点击事件
cancelBtn.addEventListener('click', function () {
document.body.removeChild(overlay);
// 恢复登录按钮状态
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
showToast('已取消绑定', 'info');
});
// 点击蒙层关闭弹窗
overlay.addEventListener('click', function (e) {
if (e.target === overlay) {
cancelBtn.click();
}
});
// 回车键提交
idCardInput.addEventListener('keypress', function (e) {
if (e.key === 'Enter' && !bindBtn.disabled) {
//bindBtn.click();
}
});
// 自动聚焦到输入框
setTimeout(() => {
idCardInput.focus();
}, 100);
}
/**
* 调用活动绑定接口
* @param {string} idCard - 身份证号码
* @param {HTMLElement} overlay - 弹窗蒙层元素
*/
function bindSSNRegisterInfo(idCard, overlay) {
const bindBtn = document.getElementById('ssn-bind-btn');
const cancelBtn = document.getElementById('ssn-cancel-btn');
// 禁用按钮,显示加载状态
bindBtn.disabled = true;
bindBtn.style.opacity = '0.7';
bindBtn.style.cursor = 'not-allowed';
bindBtn.textContent = '绑定中...';
cancelBtn.disabled = true;
cancelBtn.style.opacity = '0.5';
// 显示加载提示
showToast('正在绑定活动报名信息...', 'info');
// 获取用户信息
const playerId = localStorage.getItem('player_id');
const token = localStorage.getItem('access_token');
// 构建请求数据
const requestData = {
id_card: idCard,
user_id: playerId
};
// 发送请求到绑定接口
fetch(`${CONFIG.DataServerBaseUrl}/api/v1/third-party/ssn-register/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
console.log('活动绑定响应:', data);
if (data.success) {
// 绑定成功
showToast('活动报名信息绑定成功', 'success');
// 存储绑定信息
if (data.data && data.data.ssn_register_info) {
localStorage.setItem('ssn_register_info', JSON.stringify(data.data.ssn_register_info));
// 存储SSN用户信息到sessionStorage,用于在competition_sum页面显示
const ssnUserInfo = {
ssn_register_id: data.data.ssn_register_info.ssn_register_id,
name: data.data.ssn_register_info.name,
id_card: data.data.ssn_register_info.id_card,
org: data.data.ssn_register_info.org
};
sessionStorage.setItem('ssn_user_info', JSON.stringify(ssnUserInfo));
}
// 关闭弹窗
document.body.removeChild(overlay);
// 延迟后继续原来的流程
setTimeout(() => {
// 继续原来的checkUserInCompetition流程
continueCheckUserInCompetition();
}, 1000);
} else {
// 绑定失败
let errorMessage = '绑定失败';
if (data.message) {
errorMessage = data.message;
} else if (data.data && data.data.third_party_error_code) {
errorMessage = `绑定失败:第三方接口错误码 ${data.data.third_party_error_code}`;
}
showToast(errorMessage, 'error');
// 恢复按钮状态
bindBtn.disabled = false;
bindBtn.style.opacity = '1';
bindBtn.style.cursor = 'pointer';
bindBtn.textContent = '确认绑定';
cancelBtn.disabled = false;
cancelBtn.style.opacity = '1';
// 关闭弹窗并恢复登录按钮状态
setTimeout(() => {
document.body.removeChild(overlay);
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
}, 2000);
}
})
.catch(error => {
console.error('活动绑定请求失败:', error);
showToast('网络错误,绑定失败', 'error');
// 恢复按钮状态
bindBtn.disabled = false;
bindBtn.style.opacity = '1';
bindBtn.style.cursor = 'pointer';
bindBtn.textContent = '确认绑定';
cancelBtn.disabled = false;
cancelBtn.style.opacity = '1';
// 关闭弹窗并恢复登录按钮状态
setTimeout(() => {
document.body.removeChild(overlay);
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
}, 2000);
});
}
/**
* 更新活动编程语言
* @param {number} codeType - 编程语言类型 (1-图形化, 2-Python, 3-C++)
* @param {string} token - 访问令牌
* @returns {Promise} - 返回Promise,成功时resolve,失败时reject
*/
function updateSSNCodingLanguage(codeType, token) {
return new Promise((resolve, reject) => {
// 获取用户ID
const playerId = localStorage.getItem('player_id');
if (!playerId) {
const error = '无法获取用户ID,跳过活动编程语言更新';
console.error(error);
showToast(error, 'error');
reject(new Error(error));
return;
}
// 显示更新提示
showToast('正在更新活动编程语言...', 'info');
// 构建请求数据
const requestData = {
user_id: playerId,
coding_language_type: codeType
};
// 发送请求更新编程语言
fetch(`${CONFIG.DataServerBaseUrl}/api/v1/third-party/update-coding-language/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
console.log('更新活动编程语言响应:', data);
if (data.success) {
// 更新成功
const languageNames = {
1: '图形化',
2: 'Python',
3: 'C++'
};
const languageName = languageNames[codeType] || '未知';
showToast(`活动编程语言已更新为:${languageName}`, 'success');
// 更新本地存储的SSN信息
if (data.data && data.data.updated_record) {
const ssnInfo = JSON.parse(localStorage.getItem('ssn_register_info') || '{}');
ssnInfo.coding_language = data.data.new_coding_language;
localStorage.setItem('ssn_register_info', JSON.stringify(ssnInfo));
}
// 成功时resolve
resolve(data);
} else {
// 更新失败
let errorMessage = '活动编程语言更新失败';
if (data.message) {
errorMessage = data.message;
} else if (data.data && data.data.third_party_error_code) {
errorMessage = `更新失败:第三方接口错误码 ${data.data.third_party_error_code}`;
}
showToast(errorMessage, 'error');
console.error('活动编程语言更新失败:', data);
// 失败时reject
reject(new Error(errorMessage));
}
})
.catch(error => {
const errorMessage = '网络错误,活动编程语言更新失败';
console.error('更新活动编程语言请求失败:', error);
showToast(errorMessage, 'error');
// 网络错误时reject
reject(error);
});
});
}
/**
* 继续执行checkUserInCompetition流程(绑定成功后)
*/
function continueCheckUserInCompetition() {
// 绑定成功后,需要执行完整的参与者查询逻辑
// 获取必要的参数
const token = localStorage.getItem('access_token');
const competitionId = getUrlParameter('competition_id');
const platformType = localStorage.getItem('platformType');
if (!token || !competitionId) {
showToast('用户信息获取失败', 'error');
// 恢复按钮状态
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
return;
}
// 构建请求数据
const requestData = {
competition_id: parseInt(competitionId, 10),
page: 1,
page_size: 10,
username: localStorage.getItem('username')
};
// 发送请求检查用户是否在比赛中
fetch(`${CONFIG.DataServerBaseUrl}/api/competition/query_competition_participants/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
console.log('绑定后的比赛参与者查询响应:', data);
// 检查比赛是否需要授权
const competition = window.competitionData;
const isAuthorized = competition && competition.is_authorized;
if (data.success && data.data && data.data.participants && data.data.participants.length > 0) {
// 用户在比赛中,跳转到比赛页面
localStorage.setItem("codetype", parseInt(data.data.participants[0].codetype, 10))
showToast('验证成功,正在进入赛练...', 'success');
window.location.href = 'competition_sum.html?competition_id=' + competitionId;
} else if (isAuthorized) {
// 比赛需要授权但用户未被授权
showToast('您尚无法参与此活动,请联系管理员', 'error');
// 恢复按钮状态
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
} else {
// 用户不在比赛中,需要检查是否为活动比赛以及语言选择状态
const competitionIdInt = parseInt(competitionId, 10);
const isSSNCompetition = Array.isArray(CONFIG.SSN_COMPETITION_ID)
? CONFIG.SSN_COMPETITION_ID.includes(competitionIdInt)
: competitionIdInt === CONFIG.SSN_COMPETITION_ID;
if (isSSNCompetition) {
// 活动比赛,检查语言选择状态
const ssnInfo = JSON.parse(localStorage.getItem('ssn_register_info') || '{}');
const hasChangedLanguage = ssnInfo.has_changed_language;
const codingLanguage = ssnInfo.coding_language;
if (hasChangedLanguage) {
// 已经修改过语言,显示选择框但用户不可修改
showToast('请确认参赛类型(您已选择过编程语言)', 'info');
// 弹出选择codetype的modal
const competitionInfoLink = document.getElementById('competition-info-link');
if (competitionInfoLink) {
competitionInfoLink.click();
// 延迟设置选择框状态,确保模态框已打开
setTimeout(() => {
setCodetypeModalForSSN(codingLanguage, true); // true表示不可修改
}, 500);
}
} else {
// 未修改过语言,用户可以选择
showToast('请选择参赛类型', 'info');
// 弹出选择codetype的modal
const competitionInfoLink = document.getElementById('competition-info-link');
if (competitionInfoLink) {
competitionInfoLink.click();
// 延迟设置选择框状态,确保模态框已打开
setTimeout(() => {
setCodetypeModalForSSN(codingLanguage, false); // false表示可以修改
}, 500);
}
}
} else {
// 非活动比赛,根据 type 参数处理
const types = getTypeListFromUrl();
if (types.length === 1) {
// 只有一个类型,直接加入比赛
joinCompetition(competitionId, types[0]);
} else {
// 多个类型,弹出选择模态框
showCodetypeModal(types);
showToast('请选择参赛类型', 'info');
}
}
// 恢复按钮状态
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
}
})
.catch(error => {
console.error('绑定后检查用户是否在比赛中失败:', error);
showToast('验证赛练信息失败,请稍后重试', 'error');
// 恢复按钮状态
const submitButton = document.querySelector('.u-btn-submit');
if (submitButton) {
submitButton.classList.remove('btn-loading');
submitButton.removeAttribute('disabled');
submitButton.style.pointerEvents = 'auto';
}
});
}
/**
* 设置活动比赛的编程语言选择框状态
* @param {string} codingLanguage - 编程语言
* @param {boolean} isReadOnly - 是否为只读状态
*/
function setCodetypeModalForSSN(codingLanguage, isReadOnly) {
const codeTypeSelect = document.getElementById('select-8c9b');
if (!codeTypeSelect) {
console.error('未找到编程语言选择框');
return;
}
// 编程语言映射:API返回的字符串 -> 选择框的value
const languageMapping = {
'图形化': '1',
'Python': '2',
'C++': '3'
};
// 设置选择框的值
const selectValue = languageMapping[codingLanguage] || '1'; // 默认为图形化
codeTypeSelect.value = selectValue;
if (isReadOnly) {
// 设置为只读状态
codeTypeSelect.disabled = true;
codeTypeSelect.style.opacity = '0.6';
codeTypeSelect.style.cursor = 'not-allowed';
// 为父容器添加样式指示这是只读的
const selectWrapper = codeTypeSelect.closest('.u-form-select-wrapper');
if (selectWrapper) {
selectWrapper.style.opacity = '0.6';
selectWrapper.style.pointerEvents = 'none';
}
// 添加提示文本
const modalContainer = codeTypeSelect.closest('.u-dialog');
if (modalContainer) {
let readOnlyHint = modalContainer.querySelector('.ssn-readonly-hint');
if (!readOnlyHint) {
readOnlyHint = document.createElement('p');
readOnlyHint.className = 'ssn-readonly-hint';
readOnlyHint.style.cssText = `
color: rgb(0, 0, 0);
font-size: 0.75rem;
text-align: center;
margin: 5px 0;
font-weight: normal;
width: 100%;
max-width: 100%;
box-sizing: border-box;
word-wrap: break-word;
white-space: normal;
line-height: 1.3;
padding: 0;
`;
readOnlyHint.textContent = '您已选择过编程语言,无法修改';
// 插入到提交按钮的上方
const submitContainer = modalContainer.querySelector('.u-form-submit');
if (submitContainer) {
submitContainer.parentNode.insertBefore(readOnlyHint, submitContainer);
} else {
// 如果找不到提交按钮容器,则插入到选择框后面
const selectContainer = codeTypeSelect.closest('.u-form-group');
if (selectContainer) {
selectContainer.parentNode.insertBefore(readOnlyHint, selectContainer.nextSibling);
}
}
}
}
} else {
// 设置为可编辑状态
codeTypeSelect.disabled = false;
codeTypeSelect.style.opacity = '1';
codeTypeSelect.style.cursor = 'pointer';
// 恢复父容器样式
const selectWrapper = codeTypeSelect.closest('.u-form-select-wrapper');
if (selectWrapper) {
selectWrapper.style.opacity = '1';
selectWrapper.style.pointerEvents = 'auto';
}
// 移除只读提示文本
const modalContainer = codeTypeSelect.closest('.u-dialog');
if (modalContainer) {
const readOnlyHint = modalContainer.querySelector('.ssn-readonly-hint');
if (readOnlyHint) {
readOnlyHint.remove();
}
}
// 添加可选择提示文本
const modalContainer2 = codeTypeSelect.closest('.u-dialog');
if (modalContainer2) {
let editableHint = modalContainer2.querySelector('.ssn-editable-hint');
if (!editableHint) {
editableHint = document.createElement('p');
editableHint.className = 'ssn-editable-hint';
editableHint.style.cssText = `
color: rgb(0, 0, 0);
font-size: 0.75rem;
text-align: center;
margin: 5px 0;
font-weight: normal;
width: 100%;
max-width: 100%;
box-sizing: border-box;
word-wrap: break-word;
white-space: normal;
line-height: 1.3;
padding: 0;
`;
editableHint.textContent = '请选择编程语言(选择后无法修改)';
// 插入到提交按钮的上方
const submitContainer = modalContainer2.querySelector('.u-form-submit');
if (submitContainer) {
submitContainer.parentNode.insertBefore(editableHint, submitContainer);
} else {
// 如果找不到提交按钮容器,则插入到选择框后面
const selectContainer = codeTypeSelect.closest('.u-form-group');
if (selectContainer) {
selectContainer.parentNode.insertBefore(editableHint, selectContainer.nextSibling);
}
}
}
}
}
}
/**
* 注册新用户
*/
function registerUser() {
// 验证表单
if (!validateRegisterForm()) {
return;
}
// 获取表单数据
const username = document.getElementById('phone-23d9').value.trim();
const password = document.getElementById('text-b46f').value.trim();
const fullName = document.getElementById('text-bf16').value.trim();
const phoneNumber = document.getElementById('text-8d13').value.trim();
const email = document.getElementById('email-594e').value.trim();
const province = document.getElementById('select-c2b7').value;
const city = document.getElementById('select-36ac').value;
const district = document.getElementById('select-4860').value;
// 获取选中的学校ID和名称
const selectedSchoolIdInputComp = document.getElementById('selected-school-id-comp');
const schoolSelectComp = document.getElementById('school-select-comp');
const selectedSchoolId = selectedSchoolIdInputComp ? selectedSchoolIdInputComp.value : '-1';
const school = schoolSelectComp && schoolSelectComp.options[0] ? schoolSelectComp.options[0].textContent : '';
const grade = document.getElementById('select-2088').value;
// 获取注册按钮并禁用
const registerButton = document.querySelector('.u-dialog-section-6 .u-btn-step');
if (registerButton) {
registerButton.disabled = true;
registerButton.classList.add('btn-loading');
registerButton.textContent = '注册中...';
}
// 显示注册中的提示
showToast('正在注册,请稍候...', 'info');
// 使用已选择的学校ID
const institutionId = selectedSchoolId && selectedSchoolId !== '-1' ? parseInt(selectedSchoolId) : -1;
// 构建请求数据
const requestData = {
username: username,
password: password,
full_name: fullName,
phone_number: phoneNumber,
email: email,
province: province,
city: city,
district: district,
school_name: school,
grade: grade,
institution: institutionId // 使用选择的学校ID
};
console.log('注册数据:', requestData);
// 发送注册请求
fetch(`${CONFIG.DataServerBaseUrl}/api/register/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
})
.then(response => {
// 解析响应
return response.json().then(data => {
// 将响应数据和状态码一起返回
return { data, status: response.status };
});
})
.then(({ data, status }) => {
// 恢复按钮状态
if (registerButton) {
registerButton.disabled = false;
registerButton.classList.remove('btn-loading');
registerButton.textContent = '注册';
}
// 处理响应
if (status === 200 || status === 201) {
// 注册成功
showToast('注册成功!', 'success');
// 关闭注册模态框
setTimeout(() => {
// 使用正确的关闭按钮ID
const closeButton = document.querySelector('.u-dialog-section-6 .u-dialog-close-button');
if (closeButton) {
closeButton.click();
}
// 自动填充登录表单
document.getElementById('name-b064').value = username;
document.getElementById('email-b064').value = password;
// 提示用户选择练习或比赛模式
setTimeout(() => {
showToast('请选择练习或比赛模式登录此比赛', 'info', 5000);
}, 500);
}, 1500);
} else {
// 注册失败,显示错误信息
let errorMessage = '注册失败,请检查您的信息并重试';
if (data.username) {
errorMessage = `用户名错误: ${data.username.join(', ')}`;
} else if (data.email) {
errorMessage = `邮箱错误: ${data.email.join(', ')}`;
} else if (data.phone_number) {
errorMessage = `电话号码错误: ${data.phone_number.join(', ')}`;
} else if (data.password) {
errorMessage = `密码错误: ${data.password.join(', ')}`;
} else if (data.non_field_errors) {
errorMessage = data.non_field_errors.join(', ');
} else if (typeof data.message === 'string') {
errorMessage = data.message;
}
showToast(errorMessage, 'error');
}
})
.catch(error => {
// 恢复按钮状态
if (registerButton) {
registerButton.disabled = false;
registerButton.classList.remove('btn-loading');
registerButton.textContent = '注册';
}
// 显示错误信息
console.error('注册请求失败:', error);
showToast('网络错误,请稍后重试', 'error');
});
}
/**
* 查询学校ID
* @param {string} schoolName - 学校名称
* @returns {Promise} - 返回学校ID或-1
*/
function searchInstitutionId(schoolName) {
if (!schoolName || schoolName.trim().length < 2) {
return Promise.resolve(-1); // 如果学校名称为空或太短,直接返回-1
}
// 发送请求查询学校ID
return fetch(`${CONFIG.DataServerBaseUrl}/api/institutions/search/${encodeURIComponent(schoolName)}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
if (!response.ok) {
throw new Error('网络请求失败');
}
return response.json();
})
.then(institutions => {
console.log('查询到的学校:', institutions);
// 如果找到完全匹配的学校,返回其ID
const exactMatch = institutions.find(inst => inst.name.toLowerCase() === schoolName.toLowerCase());
if (exactMatch) {
console.log('找到完全匹配的学校:', exactMatch);
return exactMatch.id;
}
// 如果没有找到完全匹配的学校,返回-1创建新学校
console.log('没有找到完全匹配的学校,将创建新学校');
return -1;
})
.catch(error => {
console.error('查询学校ID失败:', error);
return -1; // 出错时返回-1
});
}
/**
* 设置背景图片,优先使用比赛自定义背景
*/
function setBackgroundImage() {
// 获取背景图片URL,从sessionStorage中读取
const bgImageUrl = sessionStorage.getItem('competition_background_image_url');
// 获取需要设置背景的元素 - 修改为body以确保背景全屏显示
const sectionElement = document.body;
if (sectionElement) {
// 设置背景样式以确保美观
sectionElement.style.backgroundSize = 'cover';
sectionElement.style.backgroundPosition = 'center';
sectionElement.style.backgroundRepeat = 'no-repeat';
sectionElement.style.backgroundAttachment = 'fixed';
if (bgImageUrl) {
// 创建一个临时Image对象来验证URL的有效性
const img = new Image();
img.onload = function () {
// 图片加载成功,设置为背景
sectionElement.style.backgroundImage = `url("${bgImageUrl}")`;
console.log('已使用自定义背景图片:', bgImageUrl);
};
img.onerror = function () {
// 图片加载失败,使用默认背景
// 尝试使用相对路径,或者根据实际部署路径调整
sectionElement.style.backgroundImage = 'url("../images/bg.png")';
console.log('自定义背景图片加载失败,使用默认背景');
};
// 设置图片源以触发加载
img.src = bgImageUrl;
} else {
// 没有自定义背景,使用默认背景
sectionElement.style.backgroundImage = 'url("../images/bg.png")';
console.log('未设置自定义背景图片,使用默认背景');
}
}
}
/**
* 显示欢迎图片全屏蒙层
* @param {string} imageUrl - 欢迎图片URL
*/
function showWelcomeImageOverlay(imageUrl) {
// 清除创建标记,表示已开始创建
window.welcomeOverlayCreating = false;
// 创建一个全屏蒙层
const overlay = document.createElement('div');
overlay.className = 'welcome-overlay';
overlay.style.position = 'fixed';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.backgroundColor = 'rgba(255, 255, 255, 1)'; // 白色背景,不透明
overlay.style.display = 'flex';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
overlay.style.flexDirection = 'column';
overlay.style.zIndex = '99999';
overlay.style.transition = 'opacity 0.5s ease';
// 添加加载动画
const loadingSpinner = document.createElement('div');
loadingSpinner.style.width = '50px';
loadingSpinner.style.height = '50px';
loadingSpinner.style.border = '5px solid rgba(0, 0, 0, 0.1)';
loadingSpinner.style.borderRadius = '50%';
loadingSpinner.style.borderTop = '5px solid #fcc75a';
loadingSpinner.style.animation = 'spin 1s linear infinite';
// 添加加载动画的@keyframes
const style = document.createElement('style');
style.textContent = `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(style);
// 添加到DOM
overlay.appendChild(loadingSpinner);
document.body.appendChild(overlay);
// 创建欢迎图片元素
const img = document.createElement('img');
img.style.maxWidth = '100%';
img.style.maxHeight = '100%';
img.style.objectFit = 'contain';
img.style.position = 'absolute';
img.style.top = '0';
img.style.left = '0';
img.style.width = '100%';
img.style.height = '100%';
img.style.zIndex = '1';
// 创建按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.style.position = 'absolute';
buttonContainer.style.bottom = '10%';
buttonContainer.style.left = '0';
buttonContainer.style.width = '100%';
buttonContainer.style.display = 'flex';
buttonContainer.style.alignItems = 'center';
buttonContainer.style.justifyContent = 'center';
buttonContainer.style.zIndex = '2';
// 判断是否为移动设备
const isMobile = window.innerWidth <= 768;
// 根据设备类型选择图片URL
let selectedImageUrl = imageUrl;
if (isMobile) {
// 如果有移动版图片,则使用移动版
const mobileImageUrl = sessionStorage.getItem('mobile_welcome_image_url');
if (mobileImageUrl) {
selectedImageUrl = mobileImageUrl;
} else {
// 如果没有配置移动版图片,则使用默认图片
selectedImageUrl = '../images/login-bg.png';
}
}
// 电脑模式下保持使用原始的competition_welcome_image_url
// 因为imageUrl参数已经是competition_welcome_image_url,所以不需要额外处理
// 图片加载完成后显示
img.onload = function () {
// 移除加载动画
overlay.removeChild(loadingSpinner);
// 添加图片到DOM作为背景
img.className = 'welcome-image';
overlay.appendChild(img);
// 创建进入按钮
const button = document.createElement('button');
button.className = 'enter-button';
button.textContent = '点击进入活动';
// 根据设备类型设置样式
if (isMobile) {
button.style.padding = '12px 30px';
button.style.fontSize = '16px';
} else {
button.style.padding = '15px 50px';
button.style.fontSize = '18px';
}
button.style.fontWeight = 'bold';
button.style.borderRadius = '50px';
button.style.border = 'none';
button.style.outline = 'none';
button.style.cursor = 'pointer';
button.style.transition = 'all 0.3s ease';
// 使用指定的渐变色背景
button.style.backgroundImage = 'linear-gradient(to bottom, #fcc75a, #ee6622)';
button.style.color = '#ffffff';
button.style.boxShadow = '5px 5px 20px 0 rgba(0,0,0,0.4)';
// 鼠标悬停效果
button.onmouseover = function () {
this.style.transform = 'scale(1.05)';
this.style.boxShadow = '7px 7px 25px 0 rgba(0,0,0,0.5)';
};
button.onmouseout = function () {
this.style.transform = 'scale(1)';
this.style.boxShadow = '5px 5px 20px 0 rgba(0,0,0,0.4)';
};
// 点击按钮关闭蒙层
button.onclick = function () {
// 判断是否为移动设备
if (isMobile) {
// 手机模式下显示提示,不关闭欢迎页
showToast('手机端体验不佳,请在电脑端参与', 'info', 5000); // 显示5秒
} else {
// 电脑模式下正常关闭蒙层
overlay.style.opacity = '0';
setTimeout(() => {
document.body.removeChild(overlay);
// 从sessionStorage中移除欢迎图片URL,防止再次刷新时显示
sessionStorage.removeItem('competition_welcome_image_url');
// 清除创建标记
window.welcomeOverlayCreating = false;
setBackgroundImage(); // 设置背景图片
}, 500);
}
};
// 创建查看排行榜按钮
const leaderboardButton = document.createElement('button');
leaderboardButton.className = 'enter-button';
leaderboardButton.textContent = '查看排行榜';
// 根据设备类型设置样式
if (isMobile) {
leaderboardButton.style.padding = '12px 30px';
leaderboardButton.style.fontSize = '16px';
leaderboardButton.style.marginLeft = '10px'; // 移动设备上减小边距
} else {
leaderboardButton.style.padding = '15px 50px';
leaderboardButton.style.fontSize = '18px';
leaderboardButton.style.marginLeft = '20px';
}
leaderboardButton.style.fontWeight = 'bold';
leaderboardButton.style.borderRadius = '50px';
leaderboardButton.style.border = 'none';
leaderboardButton.style.outline = 'none';
leaderboardButton.style.cursor = 'pointer';
leaderboardButton.style.transition = 'all 0.3s ease';
// 使用相同的渐变色背景
leaderboardButton.style.backgroundImage = 'linear-gradient(to bottom, #fcc75a, #ee6622)';
leaderboardButton.style.color = '#ffffff';
leaderboardButton.style.boxShadow = '5px 5px 20px 0 rgba(0,0,0,0.4)';
// 鼠标悬停效果
leaderboardButton.onmouseover = function () {
this.style.transform = 'scale(1.05)';
this.style.boxShadow = '7px 7px 25px 0 rgba(0,0,0,0.5)';
};
leaderboardButton.onmouseout = function () {
this.style.transform = 'scale(1)';
this.style.boxShadow = '5px 5px 20px 0 rgba(0,0,0,0.4)';
};
// 点击按钮跳转到排行榜页面
leaderboardButton.onclick = function () {
const competitionId = getUrlParameter('competition_id');
if (competitionId) {
// 无论是手机还是电脑,都可以查看排行榜
window.location.href = `/leaderboard.html?competition_id=${competitionId}`;
} else {
showToast('未找到比赛信息', 'error');
}
};
// 如果是移动设备,调整按钮容器为垂直排列
if (isMobile && window.innerWidth < 480) {
buttonContainer.style.flexDirection = 'column';
buttonContainer.style.bottom = '15%';
leaderboardButton.style.marginLeft = '0';
leaderboardButton.style.marginTop = '15px';
}
// 添加按钮到按钮容器
buttonContainer.appendChild(button);
buttonContainer.appendChild(leaderboardButton);
overlay.appendChild(buttonContainer);
};
// 图片加载失败时的处理
img.onerror = function () {
document.body.removeChild(overlay);
// 从sessionStorage中移除欢迎图片URL,防止再次刷新时显示
sessionStorage.removeItem('competition_welcome_image_url');
// 清除创建标记
window.welcomeOverlayCreating = false;
setBackgroundImage(); // 设置背景图片
console.error('欢迎图片加载失败');
};
// 设置图片源以触发加载
img.src = selectedImageUrl;
// 添加窗口大小变化事件,自动适应屏幕尺寸
window.addEventListener('resize', function () {
const isMobileNow = window.innerWidth <= 768;
const buttonContainer = overlay.querySelector('div');
if (buttonContainer) {
const buttons = buttonContainer.querySelectorAll('button');
if (isMobileNow && window.innerWidth < 480) {
// 切换到移动模式
buttonContainer.style.flexDirection = 'column';
buttonContainer.style.bottom = '15%';
if (buttons.length > 1) {
buttons[0].style.padding = '12px 30px';
buttons[0].style.fontSize = '16px';
buttons[1].style.marginLeft = '0';
buttons[1].style.marginTop = '15px';
buttons[1].style.padding = '12px 30px';
buttons[1].style.fontSize = '16px';
}
} else {
// 切换到桌面模式
buttonContainer.style.flexDirection = 'row';
buttonContainer.style.bottom = '10%';
if (buttons.length > 1) {
buttons[0].style.padding = '15px 50px';
buttons[0].style.fontSize = '18px';
buttons[1].style.marginLeft = isMobileNow ? '10px' : '20px';
buttons[1].style.marginTop = '0';
buttons[1].style.padding = '15px 50px';
buttons[1].style.fontSize = '18px';
}
}
}
});
}
/**
* 从图片中提取主色调
* @param {HTMLImageElement} img - 图片元素
* @param {Function} callback - 回调函数,参数为提取的颜色
*/
function extractDominantColor(img, callback) {
try {
// 创建画布
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 设置画布大小(使用较小的尺寸以提高性能)
const size = 50;
canvas.width = size;
canvas.height = size;
// 在画布上绘制图片
ctx.drawImage(img, 0, 0, size, size);
// 获取像素数据
const imageData = ctx.getImageData(0, 0, size, size).data;
// 用于存储RGB值的计数
const colorCounts = {};
// 分析每个像素
for (let i = 0; i < imageData.length; i += 4) {
const r = imageData[i];
const g = imageData[i + 1];
const b = imageData[i + 2];
const a = imageData[i + 3];
// 跳过透明像素
if (a < 128) continue;
// 为了减少颜色数量,对RGB值进行量化
const quantizedR = Math.round(r / 32) * 32;
const quantizedG = Math.round(g / 32) * 32;
const quantizedB = Math.round(b / 32) * 32;
const colorKey = `${quantizedR},${quantizedG},${quantizedB}`;
// 增加这个颜色的计数
if (colorCounts[colorKey]) {
colorCounts[colorKey]++;
} else {
colorCounts[colorKey] = 1;
}
}
// 找出出现次数最多的颜色
let maxCount = 0;
let dominantColor = null;
for (const colorKey in colorCounts) {
if (colorCounts[colorKey] > maxCount) {
maxCount = colorCounts[colorKey];
dominantColor = colorKey;
}
}
if (dominantColor) {
const [r, g, b] = dominantColor.split(',').map(Number);
// 将RGB转换为HEX格式
const hexColor = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
callback(hexColor);
} else {
callback(null); // 无法提取颜色
}
} catch (error) {
console.error('提取颜色失败:', error);
callback(null); // 出错时返回null
}
}
/**
* 根据背景色获取对比色(黑色或白色文本)
* @param {string} hexColor - 十六进制颜色
* @returns {string} - 返回#000000或#ffffff
*/
function getContrastColor(hexColor) {
// 去掉#前缀
const hex = hexColor.replace('#', '');
// 将十六进制转换为RGB
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
// 计算亮度(使用YIQ公式)
const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
// 如果背景色较亮,返回黑色文本;否则返回白色文本
return (yiq >= 128) ? '#000000' : '#ffffff';
}
/**
* 显示编程语言选择模态框
* @param {Array} types - 允许的类型数组
*/
function showCodetypeModal(types) {
const select = document.getElementById('select-8c9b');
if (select) {
select.innerHTML = '';
types.forEach(t => {
const option = document.createElement('option');
option.value = t;
option.textContent = getLanguageName(t);
select.appendChild(option);
});
}
const modal = document.getElementById('carousel_cd2f');
if (modal) {
modal.style.display = 'block';
}
}
/**
* 获取编程语言的中文名称
* @param {string} t - 类型值
* @returns {string} 中文名称
*/
function getLanguageName(t) {
switch(t) {
case '1': return '图形化编程';
case '2': return 'Python编程';
case '3': return 'C++编程';
default: return '';
}
}
// 导出函数,以便其他模块可以使用
export { getUrlParameter, fetchCompetitionInfo };