- 新增 useGameBoardVm 数据层实施说明文档 - 添加 36字花核心玩法与前端规则摘要 - 创建游戏模块数据与界面分层第一阶段实施稿 - 定义四层架构:api/dto、store、view-model hooks、ui层 - 规范 PC 与 Mobile 共享业务逻辑的改造方案 - 明确各层职责边界和组件改造顺序
249 lines
6.1 KiB
Markdown
249 lines
6.1 KiB
Markdown
# 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` 直接渲染 `<DesktopAnimal />`
|
||
- 点击格子不会写入真实下注状态
|
||
|
||
结果是:
|
||
|
||
- 控制栏虽然已经接入了部分业务数据
|
||
- 状态栏、历史区也开始接入 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()
|
||
|
||
<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](/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` 不再裸挂 `<DesktopAnimal />`
|
||
- `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 层在内”的改造方向。
|
||
|