用 Vue 3 做了一套年会抽奖工具,顺便踩了些坑

去年为了年会抽奖临时做了这个小工具,今年又被点名用同一套方案。

趁着复用的机会,把之前欠下的一些小坑顺手补了一轮,整理完善之后,就决定顺便发出来给大家也能直接用:

  • 在线地址:https://huyikai.github.io/AnnualRaffle/

下面主要想分享两件事:

  • 这套工具大概能做什么;
  • 为啥我要在 UI 里留一个"临时加奖项"的入口,以及实战中这个入口是怎么救场的。

这个"年会抽奖"大概长什么样

不用太多形容词,就按模块简单说一下。

抽奖视觉

  • 中心是一个 3D 标签云,所有参与抽奖的人会在里面旋转。
  • 每个人可以配头像,抽到的时候会同时展示名字和照片。
  • 没配头像的会用默认头像兜底。

奖项相关

  • 默认有常规奖项:一等奖、二等奖、三等奖、幸运奖之类。
  • 每个奖项可以在界面上改中奖人数。
  • 可以配置 预设名单
    • 比如某个特殊奖项,中奖人是提前定好的,只是想在现场走个流程。
  • 支持 全局排除名单(领导不参与抽奖这类场景)。

抽奖过程

  • 底部选择奖项,点击开始,3D 标签云飞快转起来。
  • 有背景音乐、开始音效,可以静音、调音量,设置会保存在浏览器里。
  • 抽完之后会展示本轮中奖人,点击可以删掉,防止误操作。

数据持久化

  • 抽奖配置、名单、结果、音频设置都存在 localStorage
  • 头像文件放在 public/user/,元数据用 IndexedDB 管。

就算现场不小心刷新了浏览器,抽过的结果还在,不至于从头来过。


为什么 UI 里专门留了一个"临时加奖项"

这里有个小故事。

去年那场年会,抽到一半,领导突然来了句:

"再加一个特别奖吧,就现在抽。"

如果奖项是写死在代码里,这种需求基本意味着:

  • 改配置;
  • 重新打包;
  • 重新部署;
  • 祈祷一切顺利。

现场几百个人盯着大屏幕,这种操作就很不现实了。

所以这次我在设计的时候,刻意做了一个分层:

  • 代码里 :有一套相对稳定的默认奖项配置,放在 src/config/lottery.ts,方便版本管理和代码 review。
  • UI 里:保留"增加奖项"的交互,可以现场临时加一个奖项、设定人数,用完就算。

实战时这个入口确实救了场:主持人说加奖,我直接在配置弹窗里加了一个新奖项,填好名字和数量,点保存,就可以继续抽了,不需要重启、不需要改代码。

这类现场工具,给业务方留一点"临时操作空间",比纯粹的"配置优雅"要重要很多。


技术堆栈简单带一下

不细讲实现,只把堆栈和大致结构列一下,方便有兴趣的同学顺着代码看:

  • 基础框架
    • Vue 3 + TypeScript
    • Vite
  • 状态管理
    • Pinia:负责当前奖项、结果列表、配置等全局状态
  • UI / 样式
    • Element Plus:弹窗、表单、按钮等基础组件
    • Tailwind CSS:布局和样式
  • 可视化 / 多媒体
    • TagCanvas:3D 标签云
    • HTML5 Audio:背景音乐和音效
  • 数据存储
    • localStorage:配置、名单、抽奖结果、音频设置
    • IndexedDB:头像元数据

目录拆法比较常规:

  • composables/:抽奖逻辑、3D 标签云封装、音频控制。
  • config/:奖项配置、用户示例数据。
  • helper/:抽奖算法、IndexedDB 操作。
  • components/:配置弹窗、结果展示、公示组件等。

如果你想改 UI 或接入自家系统,大概只需要看这几个目录就够了。


真要用在年会,大概这么搞就够了

假设你也要搞一场年会抽奖,可以按这个顺序来:

1. 拉代码 & 跑起来

bash 复制代码
pnpm install
pnpm dev

浏览器打开本地地址,先用示例数据跑一遍流程,熟悉一下界面。

2. 换成你们自己的名单

在代码里:

  1. 复制 src/config/user.example.tssrc/config/user.ts
  2. 把里面的用户列表改成你们的人:
ts 复制代码
export const user: UserItem[] = [
  { key: 1001, name: '张三' },
  { key: 1002, name: '李四' },
  // ...
];

export const excludedUsers: UserItem[] = [
  { key: 9999, name: '某总' }, // 不参与所有奖项
];

不创建 user.ts 也能跑,只是走示例数据。

3. 配奖项

  • 平时 :改 src/config/lottery.ts 里的默认奖项即可。
  • 现场 :可以直接在"抽奖配置"弹窗里:
    • 调整每个奖项的数量;
    • 给某个奖项加"预设名单"(逗号分隔的用户 ID);
    • 必要时加一个临时奖项。

4. 搞定头像(强烈建议)

视觉氛围基本靠它。

做法:

  1. 准备头像图片,建议 160×160 左右,体积别太大。
  2. 放到 public/user/ 目录。
  3. 命名规则:用户 key + 扩展名,比如 1001.jpg

项目里还有一个批量处理头像的脚本,放在 scripts/ 里,用来把原始照片统一裁成头像,细节可以看 scripts/README.md


批量头像处理脚本:从"真人照片"到统一头像

真实情况是,原始员工照片的尺寸、比例、背景色基本都不一样,直接拿来做头像效果会很怪。

为此我在 scripts/ 目录下写了一套批量头像处理脚本,主要做这些事:

  • 批量裁剪 / 缩放成统一尺寸(默认 160×160)。
  • 统一背景色、统一格式(JPG/PNG)。
  • 提供"快速模式"和"AI 增强模式",按自己的环境和需求选择。

典型用法:

  1. 把原图放到 scripts/input/
  2. 运行:
bash 复制代码
pnpm run process-images
  1. scripts/output/ 拿到处理好的头像,拷到 public/user/ 即可。

详细配置(背景色、尺寸、模式开关等)在 scripts/README.md 里都有写。


部署:用 GitHub Pages 省点事

项目现在是放在 GitHub Pages 上的,地址就是:

  • https://huyikai.github.io/AnnualRaffle/

如果你也想这么搞,一般步骤是:

  1. 把代码推到 GitHub。
  2. 用 GitHub Actions 跑一遍:
    • 安装依赖;
    • pnpm build
    • dist/ 发布到 gh-pages 分支。
  3. 在仓库 Settings → Pages 里选对应分支。

配置好之后,后面每次推代码,线上链接会自动更新。


实际使用感受

这套东西在一场不到 200 人的年会上跑了一次,整体体验还可以:

  • 3D 标签云加照片,现场观感比纯文字名单要好不少。
  • 数据持久化避免了"误刷新导致重来一次"的事故。
  • UI 里的"临时加奖项"确实发挥了作用,帮我们兜住了领导临时加奖的情况。

如果你今年也被安排做年会抽奖,可以直接拿这个项目当模板:

先跑一遍 Demo,看下是否符合你们场景,再按自己需求改名单、奖项和 UI 即可。


一点开发过程的小记

这个项目的开发过程里,我是借助了 Cursor 这类辅助工具来提效的:

  • 搭项目、改结构、重构一些逻辑时,用它来帮忙生成样板代码和做局部重构。
  • 这篇文章本身,我也借助了 Cursor 里的 AI 写作辅助,再根据自己的实际场景做了调整。

如果你平时也用 VS Code 类的编辑器,可以尝试一下类似的工作流:

让工具帮你处理重复劳动,把精力放在业务和体验上。


最后:欢迎来 GitHub 点个 Star

项目在线地址我再放一次:

  • 在线 Demo:https://huyikai.github.io/AnnualRaffle/

仓库地址在这里:

  • GitHub 仓库:https://github.com/huyikai/AnnualRaffle

如果你觉得这套年会抽奖还算顺手,或者对你有一点参考价值,欢迎顺手点个 Star。

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax