纯html做的年会抽奖网页
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>年会抽奖程序 (纯离线版)</title>
<style>
/* --- 全局和基础样式 --- */
body {
font-family: 'Microsoft YaHei', 'Inter', sans-serif;
background-color: #111827;
color: #f3f4f6;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
}
.main-container {
max-width: 1200px;
width: 100%;
margin: auto;
padding: 2rem;
box-sizing: border-box;
}
.hidden {
display: none !important;
}
/* --- 文本样式 --- */
h1 {
font-size: 2.25rem;
font-weight: 700;
text-align: center;
margin-bottom: 0.5rem;
color: #ffffff;
}
h2 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1rem;
color: #ffffff;
}
h3 {
font-size: 1.125rem;
font-weight: 600;
}
p {
margin: 0;
}
.text-center {
text-align: center;
}
.text-gray-400 {
color: #9ca3af;
}
/* --- 布局 --- */
.grid {
display: grid;
gap: 2rem;
}
[url=home.php?mod=space&uid=945662]@media[/url] (min-width: 768px) {
.md-grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 1024px) {
.lg-grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.lg-col-span-2 {
grid-column: span 2 / span 2;
}
.lg-col-span-1 {
grid-column: span 1 / span 1;
}
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-between {
justify-content: space-between;
}
.justify-center {
justify-content: center;
}
.space-x-2 > * + * { margin-left: 0.5rem; }
.space-x-4 > * + * { margin-left: 1rem; }
.space-y-4 > * + * { margin-top: 1rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-8 { margin-bottom: 2rem; }
.mt-4 { margin-top: 1rem; }
.mt-6 { margin-top: 1.5rem; }
.mt-8 { margin-top: 2rem; }
/* --- 组件样式 --- */
.card {
background-color: #1f2937;
border: 1px solid #374151;
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
border: none;
color: white;
}
.btn-primary { background-color: #4f46e5; }
.btn-primary:hover { background-color: #4338ca; }
.btn-secondary { background-color: #4b5563; }
.btn-secondary:hover { background-color: #374151; }
.btn-danger { background-color: #dc2626; }
.btn-danger:hover { background-color: #b91c1c; }
.btn-lg {
font-size: 1.125rem;
padding: 0.75rem 2rem;
}
.btn-xl {
font-size: 1.25rem;
padding: 1rem 3rem;
}
input, textarea, select {
background-color: #374151;
border: 1px solid #4b5563;
color: #f3f4f6;
border-radius: 0.375rem;
padding: 0.5rem 0.75rem;
width: 100%;
box-sizing: border-box;
}
textarea {
resize: vertical;
}
/* --- 特定元素 --- */
.rolling-display {
font-size: 3rem;
font-weight: 700;
text-align: center;
padding: 4rem 1rem;
background: linear-gradient(145deg, #374151, #1f2937);
border-radius: 0.75rem;
color: #f59e0b;
min-height: 200px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-shadow: 0 0 15px #f59e0b;
}
.winner-list-item {
background-color: #374151;
padding: 0.75rem;
border-radius: 0.5rem;
margin-bottom: 0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
}
#modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.7);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background-color: #1f2937;
padding: 2rem;
border-radius: 0.75rem;
width: 90%;
max-width: 500px;
text-align: center;
box-sizing: border-box;
}
</style>
</head>
<body>
<div class="main-container">
<h1>年会抽奖程序</h1>
<p class="text-center text-gray-400 mb-8">公平、公正、公开</p>
<!-- Setup View -->
<div id="setupView">
<div class="grid md-grid-cols-2">
<!-- Participants Setup -->
<div class="card">
<h2>第一步: 导入参与人员</h2>
<p class="text-gray-400 mb-4">每行一人,格式为:姓名,工号 (用英文逗号分隔)。</p>
<textarea id="participantsInput" rows="8" class="mb-4" placeholder="例如:张三,1001李四,1002王五,1003"></textarea>
<div class="flex items-center justify-between">
<label for="fileInput" class="btn btn-secondary">从文件导入 (.txt, .csv)</label>
<input type="file" id="fileInput" class="hidden" accept=".txt,.csv">
<span id="participantCount" class="text-gray-400">当前人数: 0</span>
</div>
</div>
<!-- Prizes Setup -->
<div class="card">
<h2>第二步: 设置奖项</h2>
<div id="prizeList" class="space-y-4 mb-4">
<!-- Prize items will be added here -->
</div>
<button id="addPrizeBtn" class="btn btn-secondary" style="width: 100%;">添加新奖项</button>
</div>
</div>
<div class="text-center mt-8">
<button id="startDrawBtn" class="btn btn-primary btn-lg">开始抽奖!</button>
</div>
</div>
<!-- Draw View -->
<div id="drawView" class="hidden">
<div class="grid lg-grid-cols-3">
<!-- Draw Controls -->
<div class="card lg-col-span-2">
<div class="flex justify-between items-center mb-4">
<h2>抽奖环节</h2>
<button id="backToSetupBtn" class="btn btn-secondary">返回设置</button>
</div>
<div class="mb-4">
<label for="prizeSelect" style="display:block; margin-bottom: 0.5rem; font-weight: 500;" class="text-gray-300">当前抽取的奖项:</label>
<select id="prizeSelect"></select>
</div>
<div class="rolling-display" id="rollingDisplay">
准备开始...
</div>
<div class="mt-6 flex justify-center space-x-4">
<button id="toggleDrawBtn" class="btn btn-primary btn-xl">开始滚动</button>
<button id="resetBtn" class="btn btn-danger btn-xl">重置程序</button>
</div>
</div>
<!-- Winners List -->
<div class="card lg-col-span-1">
<h2>🎉 中奖名单</h2>
<div id="winnerDisplay" class="space-y-4" style="max-height: 24rem; overflow-y: auto;">
<!-- Winner lists will be generated here -->
</div>
<div class="mt-4">
<button id="exportWinnersBtn" class="btn btn-secondary" style="width: 100%;">导出中奖名单</button>
</div>
</div>
</div>
</div>
</div>
<!-- Modal for alerts -->
<div id="modal">
<div class="modal-content">
<h3 id="modalTitle" style="color:white; margin-bottom: 1rem;">提示</h3>
<p id="modalMessage" class="text-gray-300" style="margin-bottom: 1.5rem;"></p>
<button id="modalCloseBtn" class="btn btn-primary">好的</button>
</div>
</div>
<script>
// --- DOM Elements ---
const setupView = document.getElementById('setupView');
const drawView = document.getElementById('drawView');
const participantsInput = document.getElementById('participantsInput');
const fileInput = document.getElementById('fileInput');
const participantCount = document.getElementById('participantCount');
const prizeList = document.getElementById('prizeList');
const addPrizeBtn = document.getElementById('addPrizeBtn');
const startDrawBtn = document.getElementById('startDrawBtn');
const backToSetupBtn = document.getElementById('backToSetupBtn');
const prizeSelect = document.getElementById('prizeSelect');
const rollingDisplay = document.getElementById('rollingDisplay');
const toggleDrawBtn = document.getElementById('toggleDrawBtn');
const winnerDisplay = document.getElementById('winnerDisplay');
const resetBtn = document.getElementById('resetBtn');
const exportWinnersBtn = document.getElementById('exportWinnersBtn');
// Modal elements
const modal = document.getElementById('modal');
const modalTitle = document.getElementById('modalTitle');
const modalMessage = document.getElementById('modalMessage');
const modalCloseBtn = document.getElementById('modalCloseBtn');
// --- State Management ---
let allParticipants = [];
let remainingParticipants = [];
let prizes = [];
let winners = {}; // { prizeId: [winner1, winner2] }
let rollingInterval = null;
let isRolling = false;
// --- Utility Functions ---
function showAlert(title, message) {
modalTitle.textContent = title;
modalMessage.textContent = message;
modal.style.display = 'flex';
}
modalCloseBtn.onclick = () => {
modal.style.display = 'none';
}
// --- Core Functions ---
function updateParticipantCount() {
allParticipants = participantsInput.value.split('\n').filter(line => line.trim() !== '').map(line => {
const parts = line.split(/,|,/); // Support both English and Chinese commas
return { name: parts[0]?.trim() || '', id: parts[1]?.trim() || '' };
}).filter(p => p.name && p.id);
participantCount.textContent = `当前人数: ${allParticipants.length}`;
}
function handleFileUpload(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
participantsInput.value = e.target.result;
updateParticipantCount();
};
reader.readAsText(file);
}
function addPrize() {
const prizeId = `prize-${Date.now()}`;
const prizeItem = document.createElement('div');
prizeItem.className = 'flex items-center space-x-2';
prizeItem.id = prizeId;
prizeItem.innerHTML = `
<input type="text" class="prize-name" placeholder="奖项名称 (如: 特等奖)">
<input type="number" class="prize-quantity" placeholder="数量" min="1" style="width: 80px;">
<button class="remove-prize-btn btn btn-danger" style="padding: 0.5rem;">X</button>
`;
prizeList.appendChild(prizeItem);
prizeItem.querySelector('.remove-prize-btn').addEventListener('click', () => {
prizeItem.remove();
});
}
function savePrizes() {
prizes = [];
const prizeItems = prizeList.querySelectorAll('.flex');
prizeItems.forEach(item => {
const name = item.querySelector('.prize-name').value.trim();
const quantity = parseInt(item.querySelector('.prize-quantity').value, 10);
if (name && quantity > 0) {
prizes.push({ id: item.id, name, quantity });
}
});
}
function initializeDrawView() {
// Reset state
remainingParticipants = [...allParticipants];
winners = {};
prizes.forEach(p => winners[p.id] = []);
// Populate prize select dropdown
prizeSelect.innerHTML = '';
prizes.forEach(p => {
const option = document.createElement('option');
option.value = p.id;
option.textContent = `${p.name} (剩余 ${p.quantity} 名)`;
prizeSelect.appendChild(option);
});
updateWinnerDisplay();
rollingDisplay.textContent = '准备开始...';
toggleDrawBtn.textContent = '开始滚动';
toggleDrawBtn.disabled = false;
}
function updatePrizeSelectOptions() {
const selectedPrizeId = prizeSelect.value;
prizes.forEach(p => {
const option = prizeSelect.querySelector(`option[value="${p.id}"]`);
if (option) {
const remaining = p.quantity - (winners[p.id] ? winners[p.id].length : 0);
option.textContent = `${p.name} (剩余 ${remaining} 名)`;
}
});
prizeSelect.value = selectedPrizeId;
}
function updateWinnerDisplay() {
winnerDisplay.innerHTML = '';
prizes.forEach(p => {
if (winners[p.id] && winners[p.id].length > 0) {
const prizeGroup = document.createElement('div');
prizeGroup.innerHTML = `<h3 style="color: #f59e0b;">${p.name} (${winners[p.id].length}/${p.quantity})</h3>`;
const list = document.createElement('div');
winners[p.id].forEach(winner => {
const winnerItem = document.createElement('div');
winnerItem.className = 'winner-list-item';
winnerItem.innerHTML = `
<span>${winner.name}</span>
<span class="text-gray-400">${winner.id}</span>`;
list.appendChild(winnerItem);
});
prizeGroup.appendChild(list);
winnerDisplay.appendChild(prizeGroup);
}
});
}
function startRolling() {
const currentPrizeId = prizeSelect.value;
const currentPrize = prizes.find(p => p.id === currentPrizeId);
if (!currentPrize) {
showAlert('错误', '无效的奖项!');
return;
}
const winnersForPrize = winners[currentPrizeId] || [];
if (winnersForPrize.length >= currentPrize.quantity) {
showAlert('提示', `【${currentPrize.name}】的奖项已经抽完啦!`);
return;
}
if (remainingParticipants.length === 0) {
showAlert('提示', '所有人都已经中奖啦!没有可抽的人员了。');
return;
}
isRolling = true;
toggleDrawBtn.textContent = '停止!';
toggleDrawBtn.classList.remove('btn-primary');
toggleDrawBtn.classList.add('btn-danger');
rollingInterval = setInterval(() => {
const randomIndex = Math.floor(Math.random() * remainingParticipants.length);
const randomParticipant = remainingParticipants[randomIndex];
rollingDisplay.innerHTML = `${randomParticipant.name}<br><span style="font-size: 1.5rem;">${randomParticipant.id}</span>`;
}, 50);
}
function stopRolling() {
clearInterval(rollingInterval);
rollingInterval = null;
isRolling = false;
if (remainingParticipants.length === 0) return;
const winnerIndex = Math.floor(Math.random() * remainingParticipants.length);
const winner = remainingParticipants[winnerIndex];
// Display winner
rollingDisplay.innerHTML = `${winner.name}<br><span style="font-size: 1.5rem; color: #34d399;">${winner.id}</span>`;
// Update state
const currentPrizeId = prizeSelect.value;
winners[currentPrizeId].push(winner);
remainingParticipants.splice(winnerIndex, 1);
// Update UI
updateWinnerDisplay();
updatePrizeSelectOptions();
const currentPrize = prizes.find(p => p.id === currentPrizeId);
const winnersForPrize = winners[currentPrizeId];
if (winnersForPrize.length >= currentPrize.quantity) {
showAlert('恭喜', `【${currentPrize.name}】已全部抽出!`);
toggleDrawBtn.disabled = true;
} else {
toggleDrawBtn.textContent = '开始滚动';
toggleDrawBtn.classList.remove('btn-danger');
toggleDrawBtn.classList.add('btn-primary');
}
}
function exportWinners() {
let csvContent = "奖项,姓名,工号\n";
prizes.forEach(p => {
if(winners[p.id]) {
winners[p.id].forEach(winner => {
csvContent += `${p.name},${winner.name},${winner.id}\n`;
});
}
});
const blob = new Blob([new Uint8Array([0xEF, 0xBB, 0xBF]), csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement("a");
const url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", "中奖名单.csv");
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function resetApplication() {
if (confirm('您确定要重置整个抽奖程序吗?所有设置和中奖结果都将被清空。')) {
location.reload();
}
}
// --- Event Listeners ---
participantsInput.addEventListener('input', updateParticipantCount);
fileInput.addEventListener('change', handleFileUpload);
addPrizeBtn.addEventListener('click', addPrize);
startDrawBtn.addEventListener('click', () => {
updateParticipantCount(); // Final check on participants
savePrizes();
if (allParticipants.length === 0) {
showAlert('错误', '请先添加参与抽奖的人员!');
return;
}
if (prizes.length === 0) {
showAlert('错误', '请至少设置一个奖项!');
return;
}
let totalPrizesCount = prizes.reduce((sum, p) => sum + p.quantity, 0);
if (totalPrizesCount > allParticipants.length) {
showAlert('警告', '奖品总数大于参与人数,请检查设置。');
return;
}
setupView.classList.add('hidden');
drawView.classList.remove('hidden');
initializeDrawView();
});
backToSetupBtn.addEventListener('click', () => {
if (isRolling) {
stopRolling();
}
drawView.classList.add('hidden');
setupView.classList.remove('hidden');
});
toggleDrawBtn.addEventListener('click', () => {
if (isRolling) {
stopRolling();
} else {
startRolling();
}
});
prizeSelect.addEventListener('change', () => {
if (isRolling) return; // Don't change prize while rolling
const currentPrizeId = prizeSelect.value;
const currentPrize = prizes.find(p => p.id === currentPrizeId);
const winnersForPrize = winners[currentPrizeId] || [];
if (winnersForPrize.length >= currentPrize.quantity) {
toggleDrawBtn.disabled = true;
rollingDisplay.textContent = `【${currentPrize.name}】已抽完`;
} else {
toggleDrawBtn.disabled = false;
rollingDisplay.textContent = '准备就绪';
}
});
resetBtn.addEventListener('click', resetApplication);
exportWinnersBtn.addEventListener('click', exportWinners);
// --- Initial Setup ---
addPrize(); // Add one prize field by default
</script>
</body>
</html>
版权说明
文章采用: 《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权。版权声明:本站资源来自互联网收集,仅供用于学习和交流,请勿用于商业用途。如有侵权、不妥之处,请联系客服并出示版权证明以便删除!