feat: 🎁 新增破冰抽奖功能及 82 人名单

- 新增 src/views/PrizeDraw 抽奖视图及抽奖配置 store
- 更新 defaultPersonList 为 82 位真实参与者名单
- 调整主页、路由、i18n 及音乐播放以支持抽奖入口
- 附抽奖需求及实现报告文档

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-04-24 16:29:52 +08:00
parent d26c364999
commit 25d0c95dc3
26 changed files with 21649 additions and 281 deletions

259
PRIZE_DRAW_REQUIREMENT.md Normal file
View File

@@ -0,0 +1,259 @@
# 抽奖系统需求文档
## 项目背景
当前系统是"抽人"系统从88人中抽取中奖者。现在需要新增一个"抽奖"页面为88个人每人抽取一个奖品。
## 核心需求
### 功能描述
- 创建一个新页面用于为88个人抽奖
- 每次点击抽奖按钮,为一个人抽取一个奖品
- 总共需要点击88次直到所有人都抽到奖品
- 奖品数量固定,按照以下配置:
### 奖品配置共88个
1. **快乐通勤奖** - 可提前1小时下班或晚到1小时25个
2. **跑马场自由日** - 可选在家或其他场所办公18个
3. **前途光明奖** - 获得boss 1对1畅聊1小时2个
4. **现金红包500元**3个
5. **现金红包300元**8个
6. **现金红包200元**15个
7. **现金红包100元**17个
总计25 + 18 + 2 + 3 + 8 + 15 + 17 = 88个奖品
## 技术要求
### 技术栈
- Vue 3 + TypeScript
- Pinia (状态管理)
- 复用现有的组件和样式系统
### 数据结构
#### 人员数据
```typescript
// 使用现有的 88 个人员数据LN-001 到 LN-088
// 从 src/store/data.ts 的 defaultPersonList 获取
```
#### 奖品数据结构
```typescript
interface Prize {
id: string
name: string
description: string
count: number // 总数量
remaining: number // 剩余数量
color: string // 显示颜色
icon?: string // 图标(可选)
}
interface DrawResult {
personId: string // 人员ID (LN-001)
personName: string // 人员姓名
prizeId: string // 奖品ID
prizeName: string // 奖品名称
drawTime: string // 抽奖时间
drawIndex: number // 第几次抽奖 (1-88)
}
```
### 页面布局
#### 主要区域
1. **顶部标题区**
- 显示"年会抽奖"
- 显示当前进度:已抽 X/88
2. **奖品池展示区**(左侧或顶部)
- 显示所有奖品类型
- 每个奖品显示:名称、描述、剩余数量/总数量
- 已抽完的奖品置灰
3. **抽奖主区域**(中央)
- 大按钮:"开始抽奖" / "继续抽奖"
- 抽奖动画效果(可复用现有的卡片翻转/滚动动画)
- 显示当前抽奖结果:
- 人员编号和姓名
- 抽中的奖品
- 庆祝动画
4. **已抽奖记录区**(右侧或底部)
- 显示已完成的抽奖记录
- 可滚动列表
- 每条记录显示:序号、人员、奖品
### 核心逻辑
#### 抽奖算法
```typescript
// 1. 初始化奖品池88个奖品
const prizePool = [
...Array(25).fill('快乐通勤奖'),
...Array(18).fill('跑马场自由日'),
...Array(2).fill('前途光明奖'),
...Array(3).fill('现金红包500元'),
...Array(8).fill('现金红包300元'),
...Array(15).fill('现金红包200元'),
...Array(17).fill('现金红包100元'),
]
// 2. 初始化人员池88个人
const personPool = [...defaultPersonList]
// 3. 每次抽奖
function draw() {
// 随机选择一个人(从未抽奖的人中)
const randomPersonIndex = Math.floor(Math.random() * personPool.length)
const person = personPool.splice(randomPersonIndex, 1)[0]
// 随机选择一个奖品(从剩余奖品中)
const randomPrizeIndex = Math.floor(Math.random() * prizePool.length)
const prize = prizePool.splice(randomPrizeIndex, 1)[0]
// 记录结果
return { person, prize }
}
```
#### 状态管理Pinia Store
```typescript
// src/store/prizeDrawConfig.ts
export const usePrizeDrawConfig = defineStore('prizeDraw', {
state: () => ({
prizes: [...], // 奖品配置
persons: [...], // 人员列表
drawResults: [], // 抽奖结果
currentDrawIndex: 0, // 当前抽奖次数
isDrawing: false, // 是否正在抽奖
}),
actions: {
// 初始化
init() {},
// 执行抽奖
async executeDraw() {},
// 重置
reset() {},
// 导出结果
exportResults() {},
}
})
```
### 路由配置
```typescript
// src/router/index.ts
{
path: '/prize-draw',
name: 'PrizeDraw',
component: () => import('@/views/PrizeDraw/index.vue'),
meta: { title: '抽奖系统' }
}
```
### 页面入口
- 在主页添加导航按钮,可以切换到抽奖页面
- 或者在顶部菜单添加"抽奖"选项
## UI/UX 要求
### 视觉效果
1. **抽奖动画**
- 点击按钮后,显示加载/滚动动画1-2秒
- 可复用现有的卡片滚动效果
- 最终定格显示结果
2. **结果展示**
- 大字显示人员编号和奖品名称
- 使用主题色(绿色系)
- 添加庆祝效果(礼花、闪光等)
3. **进度指示**
- 进度条显示整体进度
- 奖品池实时更新剩余数量
### 交互流程
1. 进入页面 → 显示初始状态88人待抽奖88个奖品
2. 点击"开始抽奖" → 播放动画 → 显示结果第1个人抽到XX奖品
3. 点击"继续抽奖" → 重复步骤2
4. 第88次抽奖完成 → 显示"抽奖完成",提供导出功能
### 数据持久化
- 使用 localStorage 或 IndexedDB 保存抽奖进度
- 刷新页面后可以继续未完成的抽奖
- 提供"重新开始"功能清空数据
## 额外功能(可选)
### 导出功能
- 导出抽奖结果为 Excel/CSV
- 包含:序号、人员编号、人员姓名、奖品名称、抽奖时间
### 统计功能
- 显示每个奖品的分布情况
- 可视化图表展示
### 撤销功能
- 允许撤销最后一次抽奖
- 将人员和奖品放回池中
## 实现步骤建议
### Phase 1: 基础功能
1. 创建 Pinia store (`prizeDrawConfig.ts`)
2. 创建页面组件 (`src/views/PrizeDraw/index.vue`)
3. 实现基础抽奖逻辑
4. 添加路由配置
### Phase 2: UI 优化
1. 设计页面布局
2. 添加抽奖动画
3. 美化结果展示
4. 添加进度指示
### Phase 3: 增强功能
1. 数据持久化
2. 导出功能
3. 统计展示
4. 撤销功能
## 文件结构
```
src/
├── views/
│ └── PrizeDraw/
│ ├── index.vue # 主页面
│ ├── components/
│ │ ├── PrizePool.vue # 奖品池展示
│ │ ├── DrawButton.vue # 抽奖按钮
│ │ ├── DrawResult.vue # 结果展示
│ │ └── DrawHistory.vue # 历史记录
│ └── composables/
│ └── usePrizeDraw.ts # 抽奖逻辑
├── store/
│ └── prizeDrawConfig.ts # 状态管理
└── router/
└── index.ts # 路由配置(更新)
```
## 注意事项
1. 确保随机算法的公平性
2. 处理边界情况(最后一个人、最后一个奖品)
3. 考虑性能优化大量DOM更新
4. 移动端适配
5. 复用现有主题配置(颜色、字体等)
## 验收标准
- [ ] 可以完整执行88次抽奖
- [ ] 每个人只能抽一次
- [ ] 每个奖品数量准确
- [ ] 动画流畅自然
- [ ] 数据可以持久化
- [ ] 可以导出结果
- [ ] 移动端可用
- [ ] 代码结构清晰,可维护