feat: 实现麻将游戏结算系统与自摸胡功能
新增结算类型枚举和分数变更记录模型 补全响应裁决器与结算服务,支持点炮胡、自摸胡和明杠结算 扩展座位模型,增加已胡状态和分数字段 完善胡牌评估器,支持自摸胡判断 前端原型页增加分数显示和已胡状态 更新SPRINT文档记录当前进度
This commit is contained in:
@@ -30,7 +30,9 @@ type SelfSeatView = {
|
||||
seatNo: number
|
||||
playerId: string
|
||||
nickname: string
|
||||
won: boolean
|
||||
lackSuit: string | null
|
||||
score: number
|
||||
handTiles: string[]
|
||||
discardTiles: string[]
|
||||
}
|
||||
@@ -40,7 +42,9 @@ type PublicSeatView = {
|
||||
playerId: string
|
||||
nickname: string
|
||||
ai: boolean
|
||||
won: boolean
|
||||
lackSuit: string | null
|
||||
score: number
|
||||
handCount: number
|
||||
discardTiles: string[]
|
||||
}
|
||||
@@ -125,7 +129,11 @@ const actionScopeLabelMap: Record<string, string> = {
|
||||
|
||||
const canSelectLack = computed(() => game.value?.phase === 'LACK_SELECTION')
|
||||
const canDiscard = computed(
|
||||
() => game.value?.phase === 'PLAYING' && game.value.selfSeat.playerId === currentUserId.value && game.value.currentSeatNo === game.value.selfSeat.seatNo
|
||||
() =>
|
||||
game.value?.phase === 'PLAYING' &&
|
||||
!game.value.selfSeat.won &&
|
||||
game.value.selfSeat.playerId === currentUserId.value &&
|
||||
game.value.currentSeatNo === game.value.selfSeat.seatNo
|
||||
)
|
||||
const publicSeats = computed(() => game.value?.seats ?? [])
|
||||
const privateActionCandidates = computed(() => privateAction.value?.candidates ?? [])
|
||||
@@ -330,7 +338,7 @@ async function refreshGameState() {
|
||||
})
|
||||
}
|
||||
|
||||
async function submitAction(actionType: string, tile?: string) {
|
||||
async function submitAction(actionType: string, tile?: string, sourceSeatNo?: number | null) {
|
||||
if (!game.value) {
|
||||
return
|
||||
}
|
||||
@@ -341,7 +349,8 @@ async function submitAction(actionType: string, tile?: string) {
|
||||
body: JSON.stringify({
|
||||
userId: currentUserId.value,
|
||||
actionType,
|
||||
tile: tile ?? null
|
||||
tile: tile ?? null,
|
||||
sourceSeatNo: sourceSeatNo ?? null
|
||||
})
|
||||
})
|
||||
info.value = actionType === 'DISCARD' ? `已打出 ${tile}。` : `已提交动作 ${actionType}。`
|
||||
@@ -355,6 +364,11 @@ function selectLack() {
|
||||
function discard(tile: string) {
|
||||
return submitAction('DISCARD', tile)
|
||||
}
|
||||
|
||||
function submitCandidateAction(actionType: string, tile: string | null) {
|
||||
const sourceSeatNo = privateAction.value?.sourceSeatNo ?? null
|
||||
return submitAction(actionType, tile ?? undefined, sourceSeatNo)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -512,7 +526,11 @@ function discard(tile: string) {
|
||||
<div class="self-card">
|
||||
<div class="section-title">
|
||||
<strong>我的手牌</strong>
|
||||
<span class="mini-pill">当前回合 {{ game.currentSeatNo }}</span>
|
||||
<div class="mini-tags">
|
||||
<span class="mini-pill">当前回合 {{ game.currentSeatNo }}</span>
|
||||
<span class="mini-tag">积分 {{ game.selfSeat.score }}</span>
|
||||
<span class="mini-tag">{{ game.selfSeat.won ? '已胡' : '未胡' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile-grid">
|
||||
<button
|
||||
@@ -549,13 +567,15 @@ function discard(tile: string) {
|
||||
<span v-if="privateAction.sourceSeatNo !== null" class="mini-tag">来源座位 {{ privateAction.sourceSeatNo }}</span>
|
||||
</div>
|
||||
<div class="candidate-list" v-if="privateActionCandidates.length > 0">
|
||||
<span
|
||||
<button
|
||||
v-for="(candidate, index) in privateActionCandidates"
|
||||
:key="`${candidate.actionType}-${candidate.tile ?? 'none'}-${index}`"
|
||||
class="candidate-chip"
|
||||
type="button"
|
||||
@click="submitCandidateAction(candidate.actionType, candidate.tile)"
|
||||
>
|
||||
{{ candidate.actionType }}<template v-if="candidate.tile"> · {{ candidate.tile }}</template>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<span v-if="privateAction.actionScope === 'RESPONSE'" class="message-copy">
|
||||
当前原型页已能识别响应候选消息,后续会继续补正式动作面板和真实响应流程。
|
||||
@@ -579,6 +599,8 @@ function discard(tile: string) {
|
||||
</div>
|
||||
<div class="mini-tags">
|
||||
<span class="mini-tag">{{ seat.ai ? 'AI' : '真人' }}</span>
|
||||
<span class="mini-tag">{{ seat.won ? '已胡' : '在局' }}</span>
|
||||
<span class="mini-tag">积分 {{ seat.score }}</span>
|
||||
<span class="mini-tag">手牌 {{ seat.handCount }}</span>
|
||||
<span class="mini-tag">{{ seat.lackSuit ?? '未定缺' }}</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user