feat(game): 实现麻将手牌按花色分组显示功能

- 添加 HandSuitLabel 类型定义区分万筒条三种花色
- 创建手牌花色排序映射和标签映射配置
- 实现 visibleHandTileGroups 计算属性按花色对手牌进行分组
- 新增 player-hand-group 样式类支持分组布局
- 调整 tile-chip 尺寸适配新的分组显示方式
- 修改底部控制面板位置避免遮挡手牌区域
This commit is contained in:
2026-03-27 16:14:22 +08:00
parent fd8f6d47fa
commit dc09c7e487
2 changed files with 81 additions and 22 deletions

View File

@@ -51,6 +51,7 @@ type DisplayPlayer = PlayerState & {
}
type GameActionPayload<TType extends GameAction['type']> = Extract<GameAction, { type: TType }>['payload']
type HandSuitLabel = '万' | '筒' | '条'
interface SeatViewModel {
key: SeatKey
@@ -132,6 +133,51 @@ const myHandTiles = computed(() => {
return myPlayer.value?.handTiles ?? []
})
const handSuitOrder: Record<Tile['suit'], number> = {
W: 0,
T: 1,
B: 2,
}
const handSuitLabelMap: Record<Tile['suit'], HandSuitLabel> = {
W: '万',
T: '筒',
B: '条',
}
const visibleHandTileGroups = computed(() => {
const grouped = new Map<HandSuitLabel, Tile[]>()
myHandTiles.value
.slice()
.sort((left, right) => {
const suitDiff = handSuitOrder[left.suit] - handSuitOrder[right.suit]
if (suitDiff !== 0) {
return suitDiff
}
const valueDiff = left.value - right.value
if (valueDiff !== 0) {
return valueDiff
}
return left.id - right.id
})
.forEach((tile) => {
const label = handSuitLabelMap[tile.suit]
const current = grouped.get(label) ?? []
current.push(tile)
grouped.set(label, current)
})
return (['万', '筒', '条'] as HandSuitLabel[])
.map((suit) => ({
suit,
tiles: grouped.get(suit) ?? [],
}))
.filter((group) => group.tiles.length > 0)
})
const visibleHandTiles = computed(() => {
if (gameStore.phase === 'waiting') {
return []
@@ -1517,21 +1563,27 @@ onBeforeUnmount(() => {
</button>
</div>
<div class="player-hand" v-if="visibleHandTiles.length > 0">
<button
v-for="tile in visibleHandTiles"
:key="tile.id"
class="tile-chip"
:class="{ selected: selectedTile === formatTile(tile) }"
type="button"
@click="selectTile(formatTile(tile))"
<div
v-for="group in visibleHandTileGroups"
:key="group.suit"
class="player-hand-group"
:data-suit="group.suit"
>
<img
class="tile-chip-image"
:src="getTileImage(tile)"
:alt="formatTile(tile)"
/>
</button>
<button
v-for="tile in group.tiles"
:key="tile.id"
class="tile-chip"
:class="{ selected: selectedTile === formatTile(tile) }"
type="button"
@click="selectTile(formatTile(tile))"
>
<img
class="tile-chip-image"
:src="getTileImage(tile)"
:alt="formatTile(tile)"
/>
</button>
</div>
</div>
</div>
</div>