糖逗 发表于 2026-4-10 11:02:51

2.【Vibe Coding】Trae-网页投票工具

本帖最后由 糖逗 于 2026-4-14 10:35 编辑

1. 目标
创建一个网页投票小程序(下面是我生成的效果)


2. 操作步骤

①本地创建02_Project_Vote文件夹,用Trae打开
②打开NewChat(`Ctrl+U'(Windows)、``**U`(Mac)),使用内置智能体Builder,输入提示词
③Trae会根据提示词生成程序/代码

提示词如下
你现在是一名资深网页前端开发工程师,请帮我使用原生 HTML + CSS + JavaScript
编写一个可直接在浏览器中打开使用的「在线投票程序」,要求如下:
-功能:
创建投票:输入投票标题,至少含2个选项:支持添加/删除选项
-开始投票:显示选项列表,点击为某一选项投票,实时更新票数与百分比
-防重复投票:同一设备对同一投票仅能投一次(使用lcocalstorage记录)
-结果展示:显示每个选项票数和百分比,提供简单进度条或条形图效果
-数据持久化:投票标题、选项、票数、是否已投状态均使用localStorage保存,刷新后保留
重置提供"重置投票"按钮,清空数据并重新创建新的投票

-界面与技术:
-风格简洁淡雅(浅灰/浅蓝),居中卡片布局,按钮圆角阴影
-使用CSSFlex或Grid布局;当前日期不需要显示
-不使用任何框架或库;全部代码在同一个HTML文件中
-使用原生JavaScript操作DOM,逻辑清晰,适量注释

-输出格式:
-直接保存为`vote.html`文件,
-代码可直接运行,无需任何依赖
学习视频:快速体验Trea


FishC 发表于 2026-4-10 11:04:37

以下是完整的「在线投票程序」代码,保存为 `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 {
            padding: 12px 16px;
            border: 2px solid #dfe6e9;
            border-radius: 10px;
            font-size: 1rem;
            transition: border-color 0.3s;
      }

      input: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.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 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: 2.【Vibe Coding】Trae-网页投票工具