- 打卡等级:暂无等级
- 打卡总天数:189
- 打卡月天数:13
- 打卡总奖励:145
- 最近打卡:2025-11-13 10:56:25
|
 
- <!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>
复制代码
|
|