Files
001code-html--cocos/scratch-gui/src/playground/competition_sum.ejs
2026-09-17 17:04:15 +08:00

327 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>001CODE-赛练平台</title>
<link rel="stylesheet" href="css/competition-v2.css?v=<%= htmlWebpackPlugin.options.version %>" media="screen">
<link rel="stylesheet" href="css/competition_sum-v2.css?v=<%= htmlWebpackPlugin.options.version %>" media="screen">
<script class="u-script" type="text/javascript" src="js/jquery.js?v=<%= htmlWebpackPlugin.options.version %>"
defer></script>
</head>
<body>
<div class="page">
<main class="content">
<!-- Left Column: Leaderboard -->
<div class="left-column" style="display:flex; flex-direction:column; gap:24px; min-width: 0; width: 100%;">
<section class="card ranking-card">
<header class="card-header">
<h1 class="title">
<svg class="trophy-icon" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"></path>
<path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"></path>
<path d="M4 22h16"></path>
<path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"></path>
<path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"></path>
<path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"></path>
</svg>
排行榜
</h1>
<div class="refresh-info">
实时排行榜 <span id="refresh-countdown" class="refresh-timer">30s</span> <span id="time"
class="refresh-timer"></span>
</div>
</header>
<div class="table-container">
<table class="custom-table">
<thead>
<tr>
<th style="width: 60px; text-align: center;">排名</th>
<th>姓名</th>
<th>学校</th>
<th>完成数量</th>
<th>代码总数</th>
<th>角色总步数</th>
</tr>
</thead>
<tbody id="leaderboard-body">
<!-- JS will populate -->
<tr>
<td colspan="6" style="text-align:center; padding: 20px; color: #94a3b8;">正在加载排行榜数据...</td>
</tr>
</tbody>
</table>
</div>
<!-- Search & Pagination moved inside Leaderboard Card -->
<div class="table-footer">
<div class="search-form-inline">
<label for="input-search">搜索用户:</label>
<input type="text" id="input-search" name="fullname" class="form-input-small" placeholder="输入姓名">
<button type="button" id="btn-search" class="form-btn-small">搜索</button>
<input type="hidden" id="email-2132">
</div>
<div id="page_num" class="pagination-container">
<!-- JS populates this -->
</div>
</div>
</section>
<!-- User Rank Table -->
<section class="card ranking-card">
<div class="table-container">
<table class="custom-table">
<thead>
<tr>
<th style="width: 60px; text-align: center;">排名</th>
<th>姓名</th>
<th>学校</th>
<th>完成数量</th>
<th>代码总数</th>
<th>角色总步数</th>
</tr>
</thead>
<tbody id="user-rank-body">
<tr>
<td class="rank-cell">-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
</tbody>
</table>
</div>
</section>
</div>
<!-- Right Column: Sidebar -->
<div class="right-column" style="display:flex; flex-direction:column; gap:24px;">
<!-- Countdown & Actions -->
<section class="card">
<h2 class="widget-title" id="countdown-title">
剩余时间
<span class="timer-icon"></span>
</h2>
<div id="custom-countdown" class="countdown-container">
<div class="countdown-item"><span class="countdown-value" id="countdown-days">00</span><span
class="countdown-label">日</span></div>
<div class="countdown-separator">:</div>
<div class="countdown-item"><span class="countdown-value" id="countdown-hours">00</span><span
class="countdown-label">时</span></div>
<div class="countdown-separator">:</div>
<div class="countdown-item"><span class="countdown-value" id="countdown-minutes">00</span><span
class="countdown-label">分</span></div>
<div class="countdown-separator">:</div>
<div class="countdown-item"><span class="countdown-value" id="countdown-seconds">00</span><span
class="countdown-label">秒</span></div>
</div>
<div class="action-buttons">
<button id="btn-start" class="full-width-btn u-btn-3">开始活动</button>
<button id="btn-exit" class="full-width-btn secondary">退出平台</button>
</div>
</section>
<!-- Announcement -->
<section class="card">
<h2 class="widget-title">公告</h2>
<div class="announcement-box">
<a id="competition-announcement" style="text-decoration: none; color: inherit; display: block;"></a>
</div>
</section>
<!-- Camera -->
<section class="card" style="display: none;">
<div id="camera-container" class="camera-wrapper">
<div class="camera-placeholder">摄像头画面</div>
</div>
<button id="btn-camera" class="full-width-btn">开启摄像头</button>
<p
style="font-size: 12px; color: #64748b; margin-top: 10px; text-align: center; display: flex; align-items: center; justify-content: center; gap: 4px;">
<span>📷</span> 摄像头在考试过程中将全程监考
</p>
</section>
</div>
</main>
<!-- Footer -->
<footer>
<div class="footer-line"></div>
<p class="footer-text">ICP备案号<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:#478ac9 ;">沪ICP备2026042325号</a>
公安备案号:<a href="https://beian.mps.gov.cn/#/query/webSearch?code=31011502407417" rel="noreferrer" target="_blank" style="color:#478ac9 ;"><img src="../images/gongan-beian.png" alt="" width="16" height="16" style="width:16px;height:16px;vertical-align:-3px;margin-right:4px;" />沪公网安备31011502407417号</a>
© 灵丌编程001CODE.COM</p>
</footer>
</div>
<!-- Floating Elements -->
<div id="back-to-main" class="floating-back-button">
<a href="/login.html" class="back-link">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="m12 19-7-7 7-7" />
<path d="M19 12H5" />
</svg>
<span>返回主站</span>
</a>
</div>
<div id="floating-user-name" class="floating-user-name">
<div class="user-chip">
<span id="current-user-name-text"></span>
</div>
</div>
<!-- Confirm Start Modal -->
<div id="start-competition-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>开始比赛确认</h2>
<span class="close-modal">×</span>
</div>
<div class="modal-body">
<!-- Content populated by JS or simple text -->
<p>确定要开始比赛吗?</p>
</div>
<div class="modal-footer">
<button id="confirm-start-competition" class="modal-btn confirm-btn">确认开始</button>
<button id="cancel-start-competition" class="modal-btn cancel-btn">取消</button>
</div>
</div>
</div>
<!-- Competition End / Alert Modal -->
<div id="competition-end-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h2>提示</h2>
<span class="close-modal" id="competition-end-close">×</span>
</div>
<div class="modal-body">
<p id="competition-end-message">提示信息</p>
</div>
<div class="modal-footer">
<button id="competition-end-ok" class="modal-btn confirm-btn close-modal">确定</button>
</div>
</div>
</div>
<script>
// Include drag logic for floating buttons (Adapted from original file)
document.addEventListener('DOMContentLoaded', function () {
// Logic for user name already in competition_sum.js?
// check competition_sum.js line 469. It sets 'current-user-name-text' content.
const floatingButton = document.getElementById('back-to-main');
setupDraggable(floatingButton);
const floatingUser = document.getElementById('floating-user-name');
setupDraggable(floatingUser);
function setupDraggable(element) {
if (!element) return;
let isDragging = false;
let startX, startY, startLeft, startTop;
element.addEventListener('mousedown', function (e) {
if (e.target.tagName === 'A' || e.target.closest('a')) return;
isDragging = true;
startX = e.clientX;
startY = e.clientY;
const rect = element.getBoundingClientRect();
startLeft = rect.left;
startTop = rect.top;
element.style.position = 'fixed';
element.style.left = startLeft + 'px';
element.style.top = startTop + 'px';
element.style.right = 'auto'; // Disable right positioning after drag starts
element.style.bottom = 'auto';
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
e.preventDefault();
});
function handleMouseMove(e) {
if (!isDragging) return;
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
let newLeft = startLeft + deltaX;
let newTop = startTop + deltaY;
const rect = element.getBoundingClientRect();
newLeft = Math.max(0, Math.min(newLeft, window.innerWidth - rect.width));
newTop = Math.max(0, Math.min(newTop, window.innerHeight - rect.height));
element.style.left = newLeft + 'px';
element.style.top = newTop + 'px';
}
function handleMouseUp() {
isDragging = false;
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
}
// Touch support (basic)
element.addEventListener('touchstart', function (e) {
if (e.target.tagName === 'A' || e.target.closest('a')) return;
isDragging = true;
const touch = e.touches[0];
startX = touch.clientX;
startY = touch.clientY;
const rect = element.getBoundingClientRect();
startLeft = rect.left;
startTop = rect.top;
element.style.position = 'fixed';
element.style.left = startLeft + 'px';
element.style.top = startTop + 'px';
element.style.right = 'auto';
element.style.bottom = 'auto';
e.preventDefault();
}, { passive: false });
element.addEventListener('touchmove', function (e) {
if (!isDragging) return;
const touch = e.touches[0];
const deltaX = touch.clientX - startX;
const deltaY = touch.clientY - startY;
let newLeft = startLeft + deltaX;
let newTop = startTop + deltaY;
element.style.left = newLeft + 'px';
element.style.top = newTop + 'px';
e.preventDefault();
}, { passive: false });
element.addEventListener('touchend', function () { isDragging = false; });
}
// Modal close logic (competition-v2 style)
document.querySelectorAll('.close-modal, .cancel-btn').forEach(btn => {
btn.addEventListener('click', function () {
const modal = this.closest('.modal');
if (modal) modal.style.display = 'none';
});
});
// Close modal on outside click
window.addEventListener('click', function (e) {
if (e.target.classList.contains('modal')) {
e.target.style.display = 'none';
}
});
});
</script>
</body>
</html>