Files
log-lottery/PRIZE_DRAW_REQUIREMENT.md
kkfluous 25d0c95dc3 feat: 🎁 新增破冰抽奖功能及 82 人名单
- 新增 src/views/PrizeDraw 抽奖视图及抽奖配置 store
- 更新 defaultPersonList 为 82 位真实参与者名单
- 调整主页、路由、i18n 及音乐播放以支持抽奖入口
- 附抽奖需求及实现报告文档

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:29:52 +08:00

260 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 抽奖系统需求文档
## 项目背景
当前系统是"抽人"系统从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次抽奖
- [ ] 每个人只能抽一次
- [ ] 每个奖品数量准确
- [ ] 动画流畅自然
- [ ] 数据可以持久化
- [ ] 可以导出结果
- [ ] 移动端可用
- [ ] 代码结构清晰,可维护