|
|
以下是完整的「在线投票程序」代码,保存为 `vote.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>
- /* 重置默认样式 */
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- }
- body {
- background-color: #f5f7fa; /* 浅灰色背景 */
- display: flex;
- justify-content: center;
- align-items: center;
- min-height: 100vh;
- padding: 20px;
- }
- .container {
- width: 100%;
- max-width: 800px;
- background-color: #ffffff; /* 白色卡片 */
- border-radius: 16px; /* 圆角 */
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); /* 阴影 */
- padding: 30px;
- margin: 20px;
- }
- h1 {
- color: #2c3e50;
- text-align: center;
- margin-bottom: 20px;
- font-size: 2.2rem;
- }
- h2 {
- color: #3498db; /* 浅蓝色 */
- margin-top: 30px;
- margin-bottom: 15px;
- font-size: 1.6rem;
- border-bottom: 2px solid #ecf0f1;
- padding-bottom: 8px;
- }
- .section {
- margin-bottom: 25px;
- }
- .input-group {
- display: flex;
- flex-direction: column;
- gap: 12px;
- margin-bottom: 20px;
- }
- input[type="text"] {
- padding: 12px 16px;
- border: 2px solid #dfe6e9;
- border-radius: 10px;
- font-size: 1rem;
- transition: border-color 0.3s;
- }
- input[type="text"]:focus {
- outline: none;
- border-color: #3498db;
- }
- .button-group {
- display: flex;
- flex-wrap: wrap;
- gap: 12px;
- margin-top: 10px;
- }
- button {
- padding: 12px 24px;
- background-color: #3498db; /* 浅蓝色按钮 */
- color: white;
- border: none;
- border-radius: 10px; /* 按钮圆角 */
- font-size: 1rem;
- font-weight: 600;
- cursor: pointer;
- transition: background-color 0.3s, transform 0.2s;
- box-shadow: 0 4px 8px rgba(52, 152, 219, 0.2); /* 按钮阴影 */
- }
- button:hover {
- background-color: #2980b9;
- transform: translateY(-2px);
- }
- button:active {
- transform: translateY(0);
- }
- button.delete-btn {
- background-color: #e74c3c;
- box-shadow: 0 4px 8px rgba(231, 76, 60, 0.2);
- }
- button.delete-btn:hover {
- background-color: #c0392b;
- }
- button.reset-btn {
- background-color: #95a5a6;
- box-shadow: 0 4px 8px rgba(149, 165, 166, 0.2);
- }
- button.reset-btn:hover {
- background-color: #7f8c8d;
- }
- button:disabled {
- background-color: #bdc3c7;
- cursor: not-allowed;
- transform: none;
- }
- .options-list {
- list-style-type: none;
- margin-top: 15px;
- }
- .options-list li {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 12px 16px;
- background-color: #f8f9fa;
- border: 1px solid #dfe6e9;
- border-radius: 8px;
- margin-bottom: 10px;
- transition: background-color 0.3s;
- }
- .options-list li:hover {
- background-color: #ecf0f1;
- }
- .vote-options {
- display: grid;
- gap: 15px;
- margin-top: 15px;
- }
- .vote-option {
- display: flex;
- flex-direction: column;
- padding: 18px;
- background-color: #f8f9fa;
- border: 2px solid #dfe6e9;
- border-radius: 12px;
- cursor: pointer;
- transition: all 0.3s;
- }
- .vote-option:hover {
- border-color: #3498db;
- background-color: #e3f2fd;
- }
- .vote-option.voted {
- border-color: #27ae60;
- background-color: #d5f4e6;
- }
- .vote-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 10px;
- }
- .option-text {
- font-size: 1.1rem;
- font-weight: 600;
- color: #2c3e50;
- }
- .vote-count {
- font-size: 1rem;
- color: #7f8c8d;
- }
- .progress-bar {
- width: 100%;
- height: 12px;
- background-color: #dfe6e9;
- border-radius: 6px;
- overflow: hidden;
- margin-top: 8px;
- }
- .progress-fill {
- height: 100%;
- background-color: #3498db;
- border-radius: 6px;
- width: 0%;
- transition: width 0.5s ease;
- }
- .results {
- margin-top: 30px;
- }
- .result-item {
- margin-bottom: 20px;
- }
- .result-info {
- display: flex;
- justify-content: space-between;
- margin-bottom: 6px;
- }
- .percentage {
- font-weight: 600;
- color: #2c3e50;
- }
- .message {
- padding: 15px;
- border-radius: 10px;
- text-align: center;
- font-weight: 600;
- margin-top: 20px;
- }
- .message.success {
- background-color: #d5f4e6;
- color: #27ae60;
- }
- .message.error {
- background-color: #fadbd8;
- color: #e74c3c;
- }
- .message.info {
- background-color: #d6eaf8;
- color: #3498db;
- }
- .hidden {
- display: none;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <h1>在线投票程序</h1>
- <!-- 创建投票部分 -->
- <div class="section">
- <h2>创建投票</h2>
- <div class="input-group">
- <input type="text" id="voteTitle" placeholder="输入投票标题(例如:你最喜欢的编程语言?)" value="你最喜欢的编程语言?">
- <input type="text" id="newOption" placeholder="输入新选项(例如:Python)">
- <div class="button-group">
- <button id="addOptionBtn">添加选项</button>
- <button id="startVoteBtn" class="hidden">开始投票</button>
- </div>
- </div>
- <ul id="optionsList" class="options-list">
- <!-- 选项列表将动态生成 -->
- </ul>
- </div>
- <!-- 投票部分 -->
- <div id="voteSection" class="section hidden">
- <h2 id="voteTitleDisplay">投票标题</h2>
- <div id="voteOptions" class="vote-options">
- <!-- 投票选项将动态生成 -->
- </div>
- <p id="voteMessage" class="message hidden"></p>
- </div>
- <!-- 结果展示部分 -->
- <div id="resultsSection" class="section hidden">
- <h2>投票结果</h2>
- <div id="results" class="results">
- <!-- 结果将动态生成 -->
- </div>
- </div>
- <!-- 重置按钮 -->
- <div class="button-group">
- <button id="resetBtn" class="reset-btn">重置投票</button>
- </div>
- </div>
- <script>
- // 全局变量:投票数据键名
- const STORAGE_KEY = 'voteData';
- // 初始化投票数据
- let voteData = {
- title: "你最喜欢的编程语言?",
- options: [
- { text: "Python", votes: 0 },
- { text: "JavaScript", votes: 0 },
- { text: "Java", votes: 0 }
- ],
- hasVoted: false
- };
- // DOM 元素
- const voteTitleInput = document.getElementById('voteTitle');
- const newOptionInput = document.getElementById('newOption');
- const addOptionBtn = document.getElementById('addOptionBtn');
- const startVoteBtn = document.getElementById('startVoteBtn');
- const optionsList = document.getElementById('optionsList');
- const voteSection = document.getElementById('voteSection');
- const voteTitleDisplay = document.getElementById('voteTitleDisplay');
- const voteOptions = document.getElementById('voteOptions');
- const voteMessage = document.getElementById('voteMessage');
- const resultsSection = document.getElementById('resultsSection');
- const results = document.getElementById('results');
- const resetBtn = document.getElementById('resetBtn');
- // 页面加载时初始化
- document.addEventListener('DOMContentLoaded', function() {
- loadVoteData();
- renderCreateSection();
- if (voteData.options.length >= 2) {
- startVoteBtn.classList.remove('hidden');
- }
- });
- // 从 localStorage 加载投票数据
- function loadVoteData() {
- const savedData = localStorage.getItem(STORAGE_KEY);
- if (savedData) {
- try {
- voteData = JSON.parse(savedData);
- } catch (e) {
- console.error('加载投票数据失败,使用默认数据');
- }
- }
- }
- // 保存投票数据到 localStorage
- function saveVoteData() {
- localStorage.setItem(STORAGE_KEY, JSON.stringify(voteData));
- }
- // 渲染创建投票部分
- function renderCreateSection() {
- voteTitleInput.value = voteData.title;
- optionsList.innerHTML = '';
- voteData.options.forEach((option, index) => {
- const li = document.createElement('li');
- li.innerHTML = `
- <span>${option.text}</span>
- <button class="delete-btn" data-index="${index}">删除</button>
- `;
- optionsList.appendChild(li);
- });
- // 更新开始投票按钮状态
- if (voteData.options.length >= 2) {
- startVoteBtn.classList.remove('hidden');
- } else {
- startVoteBtn.classList.add('hidden');
- }
- }
- // 渲染投票部分
- function renderVoteSection() {
- voteTitleDisplay.textContent = voteData.title;
- voteOptions.innerHTML = '';
- voteData.options.forEach((option, index) => {
- const totalVotes = getTotalVotes();
- const percentage = totalVotes > 0 ? Math.round((option.votes / totalVotes) * 100) : 0;
- const optionDiv = document.createElement('div');
- optionDiv.className = 'vote-option';
- optionDiv.dataset.index = index;
- optionDiv.innerHTML = `
- <div class="vote-header">
- <span class="option-text">${option.text}</span>
- <span class="vote-count">${option.votes} 票</span>
- </div>
- <div class="progress-bar">
- <div class="progress-fill" style="width: ${percentage}%"></div>
- </div>
- `;
- voteOptions.appendChild(optionDiv);
- });
- // 如果已投票,禁用投票选项
- if (voteData.hasVoted) {
- document.querySelectorAll('.vote-option').forEach(opt => {
- opt.classList.add('voted');
- opt.style.cursor = 'not-allowed';
- });
- showMessage('您已经投过票了!', 'info');
- } else {
- showMessage('请点击选项进行投票', 'info');
- }
- voteSection.classList.remove('hidden');
- renderResults();
- }
- // 渲染结果部分
- function renderResults() {
- results.innerHTML = '';
- const totalVotes = getTotalVotes();
- voteData.options.forEach(option => {
- const percentage = totalVotes > 0 ? Math.round((option.votes / totalVotes) * 100) : 0;
- const resultItem = document.createElement('div');
- resultItem.className = 'result-item';
- resultItem.innerHTML = `
- <div class="result-info">
- <span>${option.text}</span>
- <span>${option.votes} 票 (<span class="percentage">${percentage}%</span>)</span>
- </div>
- <div class="progress-bar">
- <div class="progress-fill" style="width: ${percentage}%"></div>
- </div>
- `;
- results.appendChild(resultItem);
- });
- resultsSection.classList.remove('hidden');
- }
- // 计算总票数
- function getTotalVotes() {
- return voteData.options.reduce((sum, option) => sum + option.votes, 0);
- }
- // 显示消息
- function showMessage(text, type) {
- voteMessage.textContent = text;
- voteMessage.className = `message ${type}`;
- voteMessage.classList.remove('hidden');
- }
- // 隐藏消息
- function hideMessage() {
- voteMessage.classList.add('hidden');
- }
- // 事件监听:添加选项
- addOptionBtn.addEventListener('click', function() {
- const text = newOptionInput.value.trim();
- if (text === '') {
- alert('请输入选项内容!');
- return;
- }
- if (voteData.options.some(opt => opt.text === text)) {
- alert('该选项已存在!');
- return;
- }
- voteData.options.push({ text: text, votes: 0 });
- newOptionInput.value = '';
- saveVoteData();
- renderCreateSection();
- });
- // 事件监听:删除选项(使用事件委托)
- optionsList.addEventListener('click', function(e) {
- if (e.target.classList.contains('delete-btn')) {
- const index = parseInt(e.target.dataset.index);
- voteData.options.splice(index, 1);
- saveVoteData();
- renderCreateSection();
- }
- });
- // 事件监听:开始投票
- startVoteBtn.addEventListener('click', function() {
- voteData.title = voteTitleInput.value.trim();
- if (voteData.title === '') {
- alert('请输入投票标题!');
- return;
- }
- if (voteData.options.length < 2) {
- alert('至少需要两个选项才能开始投票!');
- return;
- }
- saveVoteData();
- renderVoteSection();
- });
- // 事件监听:投票(使用事件委托)
- voteOptions.addEventListener('click', function(e) {
- if (voteData.hasVoted) {
- showMessage('您已经投过票了,不能重复投票!', 'error');
- return;
- }
- const optionDiv = e.target.closest('.vote-option');
- if (!optionDiv) return;
- const index = parseInt(optionDiv.dataset.index);
- voteData.options[index].votes++;
- voteData.hasVoted = true;
- saveVoteData();
- renderVoteSection();
- showMessage('投票成功!感谢您的参与。', 'success');
- });
- // 事件监听:重置投票
- resetBtn.addEventListener('click', function() {
- if (confirm('确定要重置投票吗?所有数据将被清空。')) {
- voteData = {
- title: "你最喜欢的编程语言?",
- options: [
- { text: "Python", votes: 0 },
- { text: "JavaScript", votes: 0 },
- { text: "Java", votes: 0 }
- ],
- hasVoted: false
- };
- localStorage.removeItem(STORAGE_KEY);
- voteSection.classList.add('hidden');
- resultsSection.classList.add('hidden');
- hideMessage();
- renderCreateSection();
- }
- });
- // 输入框回车键快捷添加选项
- newOptionInput.addEventListener('keypress', function(e) {
- if (e.key === 'Enter') {
- addOptionBtn.click();
- }
- });
- voteTitleInput.addEventListener('keypress', function(e) {
- if (e.key === 'Enter') {
- startVoteBtn.click();
- }
- });
- </script>
- </body>
- </html>
复制代码
使用说明:
1. 将以上代码复制并保存为 vote.html 文件。
2. 在浏览器中双击打开该文件,即可使用在线投票程序。
功能说明:
- 创建投票:输入投票标题和选项,点击“添加选项”按钮增加选项,选项列表会显示并可删除。
- 开始投票:至少添加两个选项后,点击“开始投票”按钮进入投票界面。
- 投票:点击选项进行投票,系统会实时更新票数和百分比,并用进度条可视化。
- 防重复投票:同一设备只能投票一次,使用 localStorage 记录状态。
- 结果展示:投票后显示每个选项的票数和百分比进度条。
- 数据持久化:所有数据保存在 localStorage 中,刷新页面后数据保留。
- 重置投票:点击“重置投票”按钮可清空所有数据并恢复到初始状态。
界面特点:
- 采用浅灰色背景和白色卡片布局,按钮为浅蓝色圆角设计。
- 使用 Flexbox 进行响应式布局,适应不同屏幕尺寸。
- 代码完全原生,无需任何外部依赖。
如有问题,欢迎在论坛继续讨论!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 |
|