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

6.9 KiB
Raw Blame History

抽奖系统需求文档

项目背景

当前系统是"抽人"系统从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 (状态管理)
  • 复用现有的组件和样式系统

数据结构

人员数据

// 使用现有的 88 个人员数据LN-001 到 LN-088
// 从 src/store/data.ts 的 defaultPersonList 获取

奖品数据结构

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. 已抽奖记录区(右侧或底部)

    • 显示已完成的抽奖记录
    • 可滚动列表
    • 每条记录显示:序号、人员、奖品

核心逻辑

抽奖算法

// 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

// src/store/prizeDrawConfig.ts
export const usePrizeDrawConfig = defineStore('prizeDraw', {
  state: () => ({
    prizes: [...], // 奖品配置
    persons: [...], // 人员列表
    drawResults: [], // 抽奖结果
    currentDrawIndex: 0, // 当前抽奖次数
    isDrawing: false, // 是否正在抽奖
  }),
  
  actions: {
    // 初始化
    init() {},
    
    // 执行抽奖
    async executeDraw() {},
    
    // 重置
    reset() {},
    
    // 导出结果
    exportResults() {},
  }
})

路由配置

// 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次抽奖
  • 每个人只能抽一次
  • 每个奖品数量准确
  • 动画流畅自然
  • 数据可以持久化
  • 可以导出结果
  • 移动端可用
  • 代码结构清晰,可维护