我正在参加Trae「超级体验官」创意实践征文,本文所使用的 Trae 免费下载链接:www.trae.com.cn/?utm_source…
每年年会一到,咱们老板总是照例发几句牢骚,话说得那叫一个熟练:“公司养这么多程序员,连个抽奖网站都没人有空写,最后还得我自己花钱去外面买?!”
每次听到这,我内心的小剧场就开始轰轰烈烈地上演了:老板啊,咱真不是不想写,实在是日常项目一个接一个地压过来,改 Bug、赶进度、熬夜加班,哪儿还有空给您老整抽奖网站啊?再说了,年会项目这玩意,年年用一年一更新,写起来精力不小,回头您一句“今年换个花样”,我们又得推翻重来。就说实话吧,哪有那么多时间啊!
不过今年,我决定不一样了。今年不让您再破费买什么现成的抽奖工具,也不让您再对我们程序员指指点点地吐槽。抽奖网站这活儿,今年我自己接了——就让我亲手给咱年会整一个炫酷又实用的抽奖系统,顺便也在全公司面前亮一把技术肌肉,让您刮目相看!
说干就干。既然是年会抽奖,光是普通转盘可不够看,得加点花活儿才行——比如奖品弹幕、实时中奖名单、背景音乐,最好还能连上投影,动静大点儿才有年会气氛。关键是,开发周期又不能太长,毕竟业务的活儿可一个都不少。那怎么办?聪明如我,当然要借助点“神器”——Trae AIIDE,程序员提升效率的秘密武器!
可能还有人没听说过 Trae AIIDE 是啥,那我就稍微介绍一下。这是一个集智能代码补全、项目自动结构搭建、语义理解为一体的开发辅助平台,说白了,它能理解你想干什么,然后自动帮你搭出一个项目雏形,还能快速生成页面、逻辑、样式,连后端接口也能带着搞定。就像有个不眠不休、从不抱怨的“AI搭子”天天陪着你干活。这样的工具,做个抽奖助手那不是分分钟的事?
我先把抽奖流程理了一遍:主持人一声令下,页面开始倒计时,屏幕上滚动所有参会人员的头像,最终定格显示中奖人,然后弹出炫酷的特效动画和奖品信息。除此之外,后台还要有个小型的管理系统,用来添加奖品、设定奖项数量、查看中奖历史,顺便还能做点数据分析,看看哪个部门中奖最多,谁又是“非酋之王”。
发出指令:请使用html写一美观的抽奖助手,支持人员信息录入。
有了 Trae,我不再从零开始搭建项目结构,而是通过自然语言告诉它: “我要一个抽奖助手的前端页面,支持人员滚动、奖项设置和动画效果;后端要能处理奖品数据、中奖记录和用户信息。” 几分钟内,基础代码就自动生成好了,大概相当于平时我两三个小时才能撸完的初版页面。这效率,真的不是吹。
接着我开始定制前端页面,Trae 帮我把 UI 框架搭好,我稍微调整了一下配色,配上公司 Logo 和年会主题,“Lucky Draw 2025 欢欢喜喜中大奖”几个大字一打上去,年味儿立马就出来了。动画部分我用了 CSS3 和 canvas 做了爆炸效果,每次抽奖都会有小彩球飞溅,视觉冲击力直接拉满。
发出指令:帮我写一个有渐变背景和奖项动画效果的 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>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap" rel="stylesheet">
<style>
:root {
--primary: #FFD700;
--secondary: #C41E3A;
--error: #ff4757;
--gold-gradient: linear-gradient(135deg, #FFD700 0%, #B8860B 100%);
}
* {
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
background: var(--gold-gradient);
min-height: 100vh;
padding: 2rem;
position: relative;
overflow-x: hidden;
}
/* 节日装饰 */
.lantern {
position: absolute;
width: 60px;
animation: float 3s ease-in-out infinite;
filter: drop-shadow(0 5px 15px rgba(255, 215, 0, 0.5));
}
.ribbon {
position: absolute;
height: 100vh;
width: 50px;
background: repeating-linear-gradient(
45deg,
#C41E3A,
#C41E3A 10px,
#FFD700 10px,
#FFD700 20px
);
animation: sway 5s linear infinite;
}
.container {
max-width: 800px;
margin: 0 auto;
background: rgba(255,255,255,0.95);
border-radius: 1rem;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
padding: 2rem;
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 0.5rem;
color: #333;
font-weight: 600;
}
input {
width: 100%;
padding: 0.8rem;
border: 2px solid #e0e0e0;
border-radius: 0.5rem;
font-size: 1rem;
transition: border-color 0.3s ease;
}
input:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
}
.error-message {
color: var(--error);
font-size: 0.875rem;
margin-top: 0.5rem;
display: none;
}
.button {
background: var(--primary);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 0.5rem;
cursor: pointer;
font-size: 1rem;
transition: transform 0.2s, box-shadow 0.2s;
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(74, 144, 226, 0.3);
}
/* 参与者列表 */
.participant-container {
display: grid;
gap: 1rem;
margin-top: 1rem;
max-height: 300px;
overflow-y: auto;
}
.participant-item {
display: flex;
justify-content: space-between;
padding: 1rem;
background: #f8f9fa;
border-radius: 0.5rem;
animation: slideIn 0.3s ease;
}
/* 结果弹窗 */
.result-popup {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
animation: fadeIn 0.3s ease;
}
.popup-content {
background: linear-gradient(45deg, #C41E3A, #FFD700);
border: 3px solid #FFD700;
animation: blink 1.5s ease infinite;
padding: 2rem;
border-radius: 1rem;
text-align: center;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.winner-info {
margin: 1.5rem 0;
font-size: 1.2rem;
}
@keyframes slideIn {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
@keyframes sway {
0% { transform: rotate(0deg); }
25% { transform: rotate(2deg); }
75% { transform: rotate(-2deg); }
100% { transform: rotate(0deg); }
}
@keyframes blink {
0% { opacity: 0.8; }
50% { opacity: 1; text-shadow: 0 0 10px #FFD700; }
100% { opacity: 0.8; }
}
@keyframes firework {
0% { transform: scale(0); opacity: 1; }
100% { transform: scale(1); opacity: 0; }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
</style>
</head>
<body>
<!-- 节日装饰 -->
<div class="lantern" style="left:10%;top:5%">🎉</div>
<div class="lantern" style="right:10%;top:15%">🏮</div>
<div class="ribbon" style="left:-25px"></div>
<div class="ribbon" style="right:-25px"></div>
<div class="container">
<h1>幸运抽奖系统</h1>
<!-- 报名表单 -->
<form id="registrationForm">
<div class="form-group">
<label for="name">姓名:</label>
<input type="text" id="name" required>
<div class="error-message" id="nameError"></div>
</div>
<div class="form-group">
<label for="phone">手机号:</label>
<input type="tel" id="phone" pattern="\d{11}" required>
<div class="error-message" id="phoneError"></div>
</div>
<button type="submit" class="button">立即报名</button>
</form>
<!-- 参与者列表 -->
<div id="participantList"></div>
<!-- 抽奖按钮 -->
<button class="button" id="startLottery" style="margin-top: 2rem;">开始抽奖</button>
</div>
<script>
// 数据存储逻辑
let participants = JSON.parse(localStorage.getItem('participants')) || [];
const MAX_PARTICIPANTS = 200;
// 初始化渲染
renderParticipantList();
updateLotteryButton();
// 表单提交处理
document.getElementById('registrationForm').addEventListener('submit', (e) => {
e.preventDefault();
if (validateForm()) {
const name = document.getElementById('name').value.trim();
const phone = document.getElementById('phone').value.trim();
// 检查重复报名
if (participants.some(p => p.phone === phone)) {
showError('phoneError', '该手机号已报名');
return;
}
participants.push({ name, phone });
localStorage.setItem('participants', JSON.stringify(participants));
// 清空表单
e.target.reset();
document.querySelectorAll('.error-message').forEach(el => el.style.display = 'none');
renderParticipantList();
updateLotteryButton();
}
});
// 抽奖按钮点击
document.getElementById('startLottery').addEventListener('click', startLottery);
// 表单验证
function validateForm() {
const name = document.getElementById('name').value.trim();
const phone = document.getElementById('phone').value.trim();
let isValid = true;
// 清空错误提示
document.querySelectorAll('.error-message').forEach(el => el.style.display = 'none');
// 姓名验证
if (name.length < 2) {
showError('nameError', '请输入有效姓名(至少2个字符)');
isValid = false;
}
// 手机号验证
if (!/^\d{11}$/.test(phone)) {
showError('phoneError', '请输入有效的11位手机号');
isValid = false;
}
return isValid;
}
// 显示错误信息
function showError(elementId, message) {
const errorElement = document.getElementById(elementId);
errorElement.textContent = message;
errorElement.style.display = 'block';
}
// 渲染参与者列表
function renderParticipantList() {
const listDiv = document.getElementById('participantList');
listDiv.innerHTML = `<h2>已报名人员 (${participants.length}/${MAX_PARTICIPANTS})</h2>
<div class="participant-container">
${participants.map(p =>
`<div class="participant-item">
<span>${p.name}</span>
<span>${p.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')}</span>
</div>`
).join('')}
</div>`;
}
// 更新抽奖按钮状态
function updateLotteryButton() {
const btn = document.getElementById('startLottery');
btn.disabled = participants.length < 2;
btn.textContent = participants.length >= 2
? `开始抽奖 (剩余${participants.length}人)`
: '至少需要2人参与抽奖';
}
// 抽奖逻辑
function startLottery() {
const btn = document.getElementById('startLottery');
btn.disabled = true;
// 抽奖动画
let count = 0;
const animation = setInterval(() => {
btn.textContent = `抽奖进行中 ${['⣾','⣽','⣻','⢿','⡿','⣟','⣯','⣷'][count++ % 8]}`;
}, 100);
// 模拟抽奖过程
setTimeout(() => {
clearInterval(animation);
const winnerIndex = Math.floor(Math.random() * participants.length);
const winner = participants.splice(winnerIndex, 1)[0];
localStorage.setItem('participants', JSON.stringify(participants));
showResultPopup(winner);
renderParticipantList();
updateLotteryButton();
}, 2000);
}
// 显示结果弹窗
function showResultPopup(winner) {
// 礼花特效
function createFirework(x, y) {
const firework = document.createElement('div');
firework.className = 'firework';
firework.style = `left:${x}px;top:${y}px;`;
firework.innerHTML = '🎆';
document.body.appendChild(firework);
setTimeout(() => firework.remove(), 1000);
}
// 显示结果弹窗
const popup = document.createElement('div');
popup.className = 'result-popup';
popup.innerHTML = `
<div class="popup-content">
<h3>🎉 中奖者 🎉</h3>
<div class="winner-info">
<p>姓名:${winner.name}</p>
<p>电话:${winner.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')}</p>
</div>
<button onclick="this.parentElement.parentElement.remove()">关闭</button>
</div>
`;
document.body.appendChild(popup);
// 触发礼花特效
for(let i=0; i<8; i++) {
setTimeout(() => {
createFirework(Math.random()*window.innerWidth, Math.random()*window.innerHeight);
}, i*100);
}
}
</script>
</body>
</html>
从开始构思到完成测试,我一共用了不到小时天时间,就完成了这样一个功能齐全、界面美观、体验顺滑的抽奖系统。最重要的是,我还整了个“彩蛋”——每次高管中奖,背景会自动播放一段配乐,然后屏幕弹出“老板吉祥,财源滚滚来!”的标语。是不是很有意思?
项目上线后,年会那天效果果然惊艳到了所有人,尤其是老板。当他看到抽奖页面那一刻,先是愣了一下,然后嘴角就不自觉地上扬了。主持人一声“开始抽奖”,滚动头像、烟花动画、中奖提示一气呵成,全场一片掌声。最后老板忍不住感慨:“哎哟,今年这抽奖做得不错,是谁搞的?”
我默默走上台,假装谦虚地说:“老板,不用买了,以后年会抽奖,我一手包办。”
台下同事们笑声一片,我心里却暗自得意:这波,不仅证明了自己,也顺带让 Trae AIIDE 成了大家眼中的“神助攻”。以后谁还说程序员不浪漫、不懂氛围?咱不仅能写代码,还能点亮全场!