china0396 发表于 6 天前

纯html做的年会抽奖网页



https://www.asp300.cn/wp-content/uploads/2025/07/1-73.pnghttps://www.asp300.cn/wp-content/uploads/2025/07/2-39.png

<!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;
      }
      @media (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>&#127881; 中奖名单</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: }
      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?.trim() || '', id: parts?.trim() || '' };
            }).filter(p => p.name && p.id);
            participantCount.textContent = `当前人数: ${allParticipants.length}`;
      }
         
      function handleFileUpload(event) {
            const file = event.target.files;
            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 = []);

            // 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`);
                if (option) {
                  const remaining = p.quantity - (winners ? winners.length : 0);
                  option.textContent = `${p.name} (剩余 ${remaining} 名)`;
                }
            });
            prizeSelect.value = selectedPrizeId;
      }

      function updateWinnerDisplay() {
            winnerDisplay.innerHTML = '';
            prizes.forEach(p => {
                if (winners && winners.length > 0) {
                  const prizeGroup = document.createElement('div');
                  prizeGroup.innerHTML = `<h3 style="color: #f59e0b;">${p.name} (${winners.length}/${p.quantity})</h3>`;
                  const list = document.createElement('div');
                  winners.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 || [];
            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;
                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;

            // 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.push(winner);
            remainingParticipants.splice(winnerIndex, 1);

            // Update UI
            updateWinnerDisplay();
            updatePrizeSelectOptions();

            const currentPrize = prizes.find(p => p.id === currentPrizeId);
            const winnersForPrize = winners;
            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) {
                  winners.forEach(winner => {
                        csvContent += `${p.name},${winner.name},${winner.id}\n`;
                  });
                }
            });

            const blob = new Blob(), 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 || [];

            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>

页: [1]
查看完整版本: 纯html做的年会抽奖网页