Files
36-character-flower/docs/36字花-useGameBoardVm-数据层实施说明.md
JiaJun 6aaf90a6ac docs(game): 添加游戏模块数据
- 新增 useGameBoardVm 数据层实施说明文档
- 添加 36字花核心玩法与前端规则摘要
- 创建游戏模块数据与界面分层第一阶段实施稿
- 定义四层架构:api/dto、store、view-model hooks、ui层
- 规范 PC 与 Mobile 共享业务逻辑的改造方案
- 明确各层职责边界和组件改造顺序
2026-05-09 17:52:30 +08:00

6.1 KiB
Raw Blame History

36字花 useGameBoardVm 数据层实施说明

1. 目标

本说明只服务下一步开发:实现 useGameBoardVm.ts

当前目标很单一:

  • 让桌面选号盘从 store 读取真实业务数据
  • 让点击格子时真正触发下注动作
  • 不在 desktop-animal.tsx 内写业务逻辑
  • 不同时改造 mobile

本阶段不做:

  • 不改造 mobile
  • 不新增 game-ui-store
  • 不重写 DesktopAnimal 整个展示结构
  • 不处理 auto-spin / modal

2. 相关文件

本次只围绕以下文件展开:

将新增:

  • src/features/game/hooks/use-game-board-vm.ts

3. 当前问题

当前 pc-entry.tsx 里虽然已经挂载了 DesktopAnimal,但还没有把桌面主玩法接到业务链路。

当前状态:

  • DesktopAnimal 只是展示组件
  • DesktopAnimal 支持 activeIdonSelect
  • PcEntry 直接渲染 <DesktopAnimal />
  • 点击格子不会写入真实下注状态

结果是:

  • 控制栏虽然已经接入了部分业务数据
  • 状态栏、历史区也开始接入 store
  • 但桌面最核心的“点击动物下注”链路还没有打通

4. useGameBoardVm 的职责

useGameBoardVm 只做 3 件事:

  1. game-round-store 读取选号盘需要的业务数据
  2. 组织出桌面选号盘可以直接消费的 view-model
  3. 暴露点击格子的业务动作

它不负责:

  • 直接渲染 UI
  • 做 hover / 动画状态
  • 控制 modal
  • 处理移动端布局

5. 数据来源

useGameBoardVm 第一版只从 game-round-store.ts 读取:

  • cells
  • round
  • selections
  • trends
  • placeBet

可复用的派生逻辑来自 selectors.ts

  • buildGameCellViewModels

6. 第一版输出字段

第一版不追求一步到位,输出保持最小可用。

建议 useGameBoardVm 返回:

{
  cells: GameCellViewModel[]
  activeId: number | null
  canPlaceBets: boolean
  onCellPress: (cellId: number) => void
}

字段说明

cells

来源:

  • buildGameCellViewModels({ cells, round, selections, trends })

作用:

  • 给未来第二版 board 组件升级时使用
  • 即使第一版 DesktopAnimal 还没完全吃它,也应该先在 hook 里产出来

activeId

第一版定义:

  • 当前有下注的最后一个格子 id
  • 如果没有任何下注,则为 null

作用:

  • 兼容当前 desktop-animal.tsx 现有接口
  • 因为这个组件当前只支持单个 activeId,还不支持多个已选格子

canPlaceBets

定义:

  • round.phase === 'betting'

作用:

  • 控制点击是否真正触发下注
  • 也为后续 UI 禁用态预留

onCellPress

定义:

  • canPlaceBets === true 时,调用 placeBet(cellId)
  • 否则不执行

7. 第一版实现规则

7.1 不在 DesktopAnimal 内直接读 store

desktop-animal.tsx 必须继续保持展示组件定位。

不应该在里面直接写:

  • useGameRoundStore
  • placeBet
  • buildGameCellViewModels
  • round.phase 判断

7.2 业务写在 hookUI 只接 props

推荐接线方式:

pc-entry.tsx 中:

const { activeId, onCellPress } = useGameBoardVm()

<DesktopAnimal activeId={activeId} onSelect={onCellPress} />

7.3 第一版先兼容现有 DesktopAnimal 接口

当前 DesktopAnimal 只支持:

  • activeId?: number | null
  • onSelect?: (animalId: number) => void

所以第一版不要强行重做它的 props 结构。

先兼容现状,把业务链路打通即可。


8. 第一版文件改动范围

本次改动建议控制在 2 到 3 个文件:

  1. 新增 src/features/game/hooks/use-game-board-vm.ts
  2. 修改 pc-entry.tsx
  3. 如有必要,微调 desktop-animal.tsx

其中第 3 项不是必须,优先争取只改前两个文件。


9. 第一版完成标准

完成后应满足:

  • PcEntry 不再裸挂 <DesktopAnimal />
  • PcEntry 通过 useGameBoardVm 把选号盘接到 store
  • 点击格子会真实调用下注动作
  • 控制栏中的总下注额会随点击变化
  • DesktopAnimal 仍然保持展示组件定位

10. 第二版演进方向

第一版做完后,下一版再考虑升级 DesktopAnimal 接口。

当前限制:

  • DesktopAnimal 只能高亮一个 activeId

第二版建议升级为:

{
  items: GameCellViewModel[]
  onSelect: (cellId: number) => void
}

这样就可以支持:

  • 多个已下注格子同时高亮
  • 依据 status 区分 betting / selected / won / lost
  • PC 和 Mobile 共用同一份 board view-model

但这些都不属于当前这一步。


11. 结论

当前最正确的操作顺序是:

  1. 先写 useGameBoardVm.ts
  2. 再在 pc-entry.tsx 中接入
  3. 暂时不把业务写进 desktop-animal.tsx

这样可以在最小改动范围内把桌面端最核心的“点击格子下注”链路打通并继续保持“数据层在外、UI 层在内”的改造方向。