# 36字花 useGameBoardVm 数据层实施说明 ## 1. 目标 本说明只服务下一步开发:实现 `useGameBoardVm.ts`。 当前目标很单一: - 让桌面选号盘从 store 读取真实业务数据 - 让点击格子时真正触发下注动作 - 不在 [desktop-animal.tsx](/Users/jiaunun/Desktop/36-character-flower/src/features/game/components/desktop/desktop-animal.tsx) 内写业务逻辑 - 不同时改造 mobile 本阶段不做: - 不改造 mobile - 不新增 `game-ui-store` - 不重写 `DesktopAnimal` 整个展示结构 - 不处理 auto-spin / modal --- ## 2. 相关文件 本次只围绕以下文件展开: - [src/features/game/components/desktop/desktop-animal.tsx](/Users/jiaunun/Desktop/36-character-flower/src/features/game/components/desktop/desktop-animal.tsx) - [src/features/game/entry/pc-entry.tsx](/Users/jiaunun/Desktop/36-character-flower/src/features/game/entry/pc-entry.tsx) - [src/store/game/game-round-store.ts](/Users/jiaunun/Desktop/36-character-flower/src/store/game/game-round-store.ts) - [src/features/game/shared/selectors.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/selectors.ts) 将新增: - `src/features/game/hooks/use-game-board-vm.ts` --- ## 3. 当前问题 当前 [pc-entry.tsx](/Users/jiaunun/Desktop/36-character-flower/src/features/game/entry/pc-entry.tsx) 里虽然已经挂载了 `DesktopAnimal`,但还没有把桌面主玩法接到业务链路。 当前状态: - `DesktopAnimal` 只是展示组件 - `DesktopAnimal` 支持 `activeId` 和 `onSelect` - `PcEntry` 直接渲染 `` - 点击格子不会写入真实下注状态 结果是: - 控制栏虽然已经接入了部分业务数据 - 状态栏、历史区也开始接入 store - 但桌面最核心的“点击动物下注”链路还没有打通 --- ## 4. useGameBoardVm 的职责 `useGameBoardVm` 只做 3 件事: 1. 从 `game-round-store` 读取选号盘需要的业务数据 2. 组织出桌面选号盘可以直接消费的 view-model 3. 暴露点击格子的业务动作 它不负责: - 直接渲染 UI - 做 hover / 动画状态 - 控制 modal - 处理移动端布局 --- ## 5. 数据来源 `useGameBoardVm` 第一版只从 [game-round-store.ts](/Users/jiaunun/Desktop/36-character-flower/src/store/game/game-round-store.ts) 读取: - `cells` - `round` - `selections` - `trends` - `placeBet` 可复用的派生逻辑来自 [selectors.ts](/Users/jiaunun/Desktop/36-character-flower/src/features/game/shared/selectors.ts): - `buildGameCellViewModels` --- ## 6. 第一版输出字段 第一版不追求一步到位,输出保持最小可用。 建议 `useGameBoardVm` 返回: ```ts { 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](/Users/jiaunun/Desktop/36-character-flower/src/features/game/components/desktop/desktop-animal.tsx) 现有接口 - 因为这个组件当前只支持单个 `activeId`,还不支持多个已选格子 #### `canPlaceBets` 定义: - `round.phase === 'betting'` 作用: - 控制点击是否真正触发下注 - 也为后续 UI 禁用态预留 #### `onCellPress` 定义: - 当 `canPlaceBets === true` 时,调用 `placeBet(cellId)` - 否则不执行 --- ## 7. 第一版实现规则 ### 7.1 不在 DesktopAnimal 内直接读 store [desktop-animal.tsx](/Users/jiaunun/Desktop/36-character-flower/src/features/game/components/desktop/desktop-animal.tsx) 必须继续保持展示组件定位。 不应该在里面直接写: - `useGameRoundStore` - `placeBet` - `buildGameCellViewModels` - `round.phase` 判断 ### 7.2 业务写在 hook,UI 只接 props 推荐接线方式: 在 [pc-entry.tsx](/Users/jiaunun/Desktop/36-character-flower/src/features/game/entry/pc-entry.tsx) 中: ```tsx const { activeId, onCellPress } = useGameBoardVm() ``` ### 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](/Users/jiaunun/Desktop/36-character-flower/src/features/game/entry/pc-entry.tsx) 3. 如有必要,微调 [desktop-animal.tsx](/Users/jiaunun/Desktop/36-character-flower/src/features/game/components/desktop/desktop-animal.tsx) 其中第 3 项不是必须,优先争取只改前两个文件。 --- ## 9. 第一版完成标准 完成后应满足: - `PcEntry` 不再裸挂 `` - `PcEntry` 通过 `useGameBoardVm` 把选号盘接到 store - 点击格子会真实调用下注动作 - 控制栏中的总下注额会随点击变化 - `DesktopAnimal` 仍然保持展示组件定位 --- ## 10. 第二版演进方向 第一版做完后,下一版再考虑升级 `DesktopAnimal` 接口。 当前限制: - `DesktopAnimal` 只能高亮一个 `activeId` 第二版建议升级为: ```ts { 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 层在内”的改造方向。