This commit is contained in:
2026-03-24 15:25:40 +08:00
parent 292a4181ce
commit 58fe43607a
7 changed files with 757 additions and 9 deletions

View File

@@ -27,8 +27,12 @@ const {
leaveRoomPending,
canStartGame,
seatViews,
selectedTile,
actionButtons,
connectWs,
sendStartGame,
selectTile,
sendGameAction,
backHall,
} = useChengduGameRoom(route, router)
@@ -129,7 +133,7 @@ const seatDecor = computed<Record<SeatKey, SeatPlayerCardModel>>(() => {
const score = scoreMap[playerId]
result[seat.key] = {
avatar: seat.isSelf ? '我' : String(index + 1),
name: seat.isSelf ? '你自己' : playerId,
name: seat.isSelf ? '你自己' : seat.player.displayName || `玩家${seat.player.index + 1}`,
money: typeof score === 'number' ? String(score) : '--',
dealer: seat.player.index === dealerIndex,
isTurn: seat.isTurn,
@@ -170,6 +174,23 @@ const centerTimer = computed(() => {
: '等待中'
})
const pendingClaimText = computed(() => {
const claim = roomState.value.game?.state?.pendingClaim
if (!claim) {
return '无'
}
try {
return JSON.stringify(claim)
} catch {
return '存在响应窗口'
}
})
const selectedTileText = computed(() => {
return selectedTile.value ?? '未选择'
})
function missingSuitLabel(value: string | null | undefined): string {
if (!value) {
return '未定'
@@ -270,6 +291,51 @@ onBeforeUnmount(() => {
</div>
</section>
<section class="table-panel game-table-panel action-panel">
<div class="action-grid">
<div class="action-card">
<h3>我的手牌</h3>
<p class="action-hint">当前仅渲染 `my_hand` 事件下发的真实手牌</p>
<div class="hand-wall" v-if="roomState.myHand.length > 0">
<button
v-for="(tile, index) in roomState.myHand"
:key="`${tile}-${index}`"
class="tile-chip"
type="button"
:class="{ selected: selectedTile === tile }"
@click="selectTile(tile)"
>
{{ tile }}
</button>
</div>
<p v-else class="action-empty">尚未收到 `my_hand`</p>
<p class="action-meta">已选牌{{ selectedTileText }}</p>
</div>
<div class="action-card">
<h3>对局动作</h3>
<p class="action-hint">已接入 `draw / discard / peng / gang / hu / pass` WS 发包</p>
<div class="action-buttons">
<button
v-for="action in actionButtons"
:key="action.type"
class="primary-btn action-btn"
type="button"
:disabled="action.disabled"
@click="sendGameAction(action.type)"
>
{{ action.label }}
</button>
</div>
<p class="action-meta">当前响应窗口{{ pendingClaimText }}</p>
<p class="action-meta">
最近弃牌{{ roomState.game?.state?.lastDiscardTile || '无' }}
/ {{ roomState.game?.state?.lastDiscardBy || '无' }}
</p>
</div>
</div>
</section>
<section class="table-shell">
<img class="table-desk" :src="deskImage" alt="" />
<div class="table-felt">
@@ -345,3 +411,69 @@ onBeforeUnmount(() => {
</section>
</section>
</template>
<style scoped>
.action-panel {
margin-top: 12px;
}
.action-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
}
.action-card {
background: rgba(10, 27, 22, 0.72);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
padding: 16px;
}
.action-card h3 {
margin: 0 0 8px;
}
.action-hint,
.action-meta,
.action-empty {
margin: 8px 0 0;
font-size: 13px;
color: rgba(255, 255, 255, 0.72);
word-break: break-all;
}
.hand-wall,
.action-buttons {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 12px;
}
.tile-chip {
min-width: 56px;
padding: 10px 12px;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.15);
background: rgba(247, 239, 220, 0.9);
color: #1c1b18;
cursor: pointer;
}
.tile-chip.selected {
transform: translateY(-4px);
border-color: #f4c76a;
box-shadow: 0 10px 24px rgba(244, 199, 106, 0.25);
}
.action-btn {
min-width: 88px;
}
@media (max-width: 960px) {
.action-grid {
grid-template-columns: 1fr;
}
}
</style>