refactor(game): 重构缺门花色处理逻辑并优化组件结构
- 移除硬编码的花色图标导入,改用动态加载方式 - 添加新的 flowerColorMap 配置文件统一管理缺门图标 - 引入 clearActiveRoom 函数用于清理活动房间状态 - 在游戏数据解析中添加缺失花色的读取函数 - 当房间数据为空时自动清理房间状态并跳转回大厅 - 统一玩家缺门花色数据处理逻辑 - 注释掉浮动状态显示区域以优化界面布局 - 调整CSS样式中缺门标记尺寸和旋转效果 - 在游戏存储模块中添加清除快照功能 - 重构座位玩家卡片组件中的花色图标计算逻辑 - 优化花色标签映射和归一化处理函数
This commit is contained in:
@@ -481,10 +481,14 @@
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.picture-scene .player-badge.seat-right .dealer-mark {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.picture-scene .missing-mark {
|
||||
margin-left: auto;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(180deg, rgba(114, 219, 149, 0.2) 0%, rgba(21, 148, 88, 0.34) 100%);
|
||||
@@ -492,11 +496,19 @@
|
||||
}
|
||||
|
||||
.picture-scene .missing-mark img {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.picture-scene .player-badge.seat-right .missing-mark {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.picture-scene .player-badge.seat-left .missing-mark {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.picture-scene .missing-mark span {
|
||||
color: #effff5;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import wanIcon from '../../assets/images/flowerClolor/wan.png'
|
||||
import tongIcon from '../../assets/images/flowerClolor/tong.png'
|
||||
import tiaoIcon from '../../assets/images/flowerClolor/tiao.png'
|
||||
import defaultAvatarIcon from '../../assets/images/icons/avatar.svg'
|
||||
import { getLackSuitImage } from '../../config/flowerColorMap'
|
||||
import type { Suit } from '../../types/tile'
|
||||
import type { SeatPlayerCardModel } from './seat-player-card'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -11,17 +10,26 @@ const props = defineProps<{
|
||||
player: SeatPlayerCardModel
|
||||
}>()
|
||||
|
||||
function normalizeMissingSuit(value: string): Suit | null {
|
||||
const normalized = value.trim().toLowerCase()
|
||||
const missingSuitMap: Record<string, Suit> = {
|
||||
万: 'W',
|
||||
筒: 'T',
|
||||
条: 'B',
|
||||
w: 'W',
|
||||
t: 'T',
|
||||
b: 'B',
|
||||
wan: 'W',
|
||||
tong: 'T',
|
||||
tiao: 'B',
|
||||
}
|
||||
|
||||
return missingSuitMap[normalized] ?? null
|
||||
}
|
||||
|
||||
const missingSuitIcon = computed(() => {
|
||||
if (props.player.missingSuitLabel === '万') {
|
||||
return wanIcon
|
||||
}
|
||||
if (props.player.missingSuitLabel === '筒') {
|
||||
return tongIcon
|
||||
}
|
||||
if (props.player.missingSuitLabel === '条') {
|
||||
return tiaoIcon
|
||||
}
|
||||
return ''
|
||||
const suit = normalizeMissingSuit(props.player.missingSuitLabel)
|
||||
return suit ? getLackSuitImage(suit) : ''
|
||||
})
|
||||
|
||||
const resolvedAvatarUrl = computed(() => {
|
||||
|
||||
41
src/config/flowerColorMap.ts
Normal file
41
src/config/flowerColorMap.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// src/config/lackSuitMap.ts
|
||||
|
||||
|
||||
import type {Suit} from "../types/tile.ts";
|
||||
|
||||
const lackSuitImageModules = import.meta.glob(
|
||||
'/src/assets/images/flowerClolor/*.png',
|
||||
{
|
||||
eager: true,
|
||||
import: 'default',
|
||||
},
|
||||
) as Record<string, string>
|
||||
|
||||
const SUIT_FILE_MAP: Record<Suit, 'wan' | 'tong' | 'tiao'> = {
|
||||
W: 'wan',
|
||||
T: 'tong',
|
||||
B: 'tiao',
|
||||
}
|
||||
|
||||
function buildLackSuitImageKey(suit: Suit): string {
|
||||
const fileName = SUIT_FILE_MAP[suit]
|
||||
return `/src/assets/images/flowerClolor/${fileName}.png`
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据花色获取缺门图标
|
||||
* W -> wan.png
|
||||
* T -> tong.png
|
||||
* B -> tiao.png
|
||||
*/
|
||||
export function getLackSuitImage(suit: Suit): string {
|
||||
const key = buildLackSuitImageKey(suit)
|
||||
return lackSuitImageModules[key] || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为合法缺门花色
|
||||
*/
|
||||
export function isValidLackSuit(suit: string): suit is Suit {
|
||||
return suit === 'W' || suit === 'T' || suit === 'B'
|
||||
}
|
||||
@@ -29,6 +29,10 @@ export const useGameStore = defineStore('game', {
|
||||
}),
|
||||
|
||||
actions: {
|
||||
resetGame() {
|
||||
this.$reset()
|
||||
},
|
||||
|
||||
// 初始化
|
||||
initGame(data: GameState) {
|
||||
Object.assign(this, data)
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
ActiveRoomState,
|
||||
ActiveRoomSelectionInput,
|
||||
} from './state'
|
||||
import { readActiveRoomSnapshot, saveActiveRoom } from './storage'
|
||||
import { clearActiveRoomSnapshot, readActiveRoomSnapshot, saveActiveRoom } from './storage'
|
||||
|
||||
const activeRoom = ref<ActiveRoomState | null>(readActiveRoomSnapshot())
|
||||
|
||||
@@ -39,6 +39,11 @@ export function setActiveRoom(input: ActiveRoomSelectionInput) {
|
||||
saveActiveRoom(next)
|
||||
}
|
||||
|
||||
export function clearActiveRoom() {
|
||||
activeRoom.value = null
|
||||
clearActiveRoomSnapshot()
|
||||
}
|
||||
|
||||
// 使用房间状态
|
||||
export function useActiveRoomState() {
|
||||
return activeRoom
|
||||
|
||||
@@ -17,4 +17,9 @@ export function readActiveRoomSnapshot(): ActiveRoomState | null {
|
||||
// 写入缓存
|
||||
export function saveActiveRoom(state: ActiveRoomState) {
|
||||
localStorage.setItem(KEY, JSON.stringify(state))
|
||||
}
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
export function clearActiveRoomSnapshot() {
|
||||
localStorage.removeItem(KEY)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
import {computed, onBeforeUnmount, onMounted, ref} from 'vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import deskImage from '../assets/images/desk/desk_01.png'
|
||||
import wanIcon from '../assets/images/flowerClolor/wan.png'
|
||||
import tongIcon from '../assets/images/flowerClolor/tong.png'
|
||||
import tiaoIcon from '../assets/images/flowerClolor/tiao.png'
|
||||
import robotIcon from '../assets/images/icons/robot.svg'
|
||||
import exitIcon from '../assets/images/icons/exit.svg'
|
||||
import '../assets/styles/room.css'
|
||||
@@ -30,7 +27,7 @@ import {wsClient} from '../ws/client'
|
||||
import {sendWsMessage} from '../ws/sender'
|
||||
import {buildWsUrl} from '../ws/url'
|
||||
import {useGameStore} from '../store/gameStore'
|
||||
import {setActiveRoom, useActiveRoomState} from '../store'
|
||||
import {clearActiveRoom, setActiveRoom, useActiveRoomState} from '../store'
|
||||
import type {MeldState, PlayerState} from '../types/state'
|
||||
import type {Tile} from '../types/tile'
|
||||
import {getTileImage as getBottomTileImage} from '../config/bottomTileMap.ts'
|
||||
@@ -449,6 +446,14 @@ function readBoolean(source: Record<string, unknown>, ...keys: string[]): boolea
|
||||
return null
|
||||
}
|
||||
|
||||
function readMissingSuit(source: Record<string, unknown> | null | undefined): string | null {
|
||||
if (!source) {
|
||||
return null
|
||||
}
|
||||
|
||||
return readString(source, 'missing_suit', 'MissingSuit', 'ding_que', 'dingQue', 'suit', 'Suit') || null
|
||||
}
|
||||
|
||||
function tileToText(tile: Tile): string {
|
||||
return `${tile.suit}${tile.value}`
|
||||
}
|
||||
@@ -601,6 +606,15 @@ function handleRoomInfoResponse(message: unknown): void {
|
||||
const room = asRecord(payload.room)
|
||||
const gameState = asRecord(payload.game_state)
|
||||
const playerView = asRecord(payload.player_view)
|
||||
|
||||
if (!room && !gameState && !playerView) {
|
||||
clearActiveRoom()
|
||||
gameStore.resetGame()
|
||||
wsClient.close()
|
||||
void router.push('/hall')
|
||||
return
|
||||
}
|
||||
|
||||
const roomId =
|
||||
readString(room ?? {}, 'room_id', 'roomId') ||
|
||||
readString(gameState ?? {}, 'room_id', 'roomId') ||
|
||||
@@ -657,7 +671,7 @@ function handleRoomInfoResponse(message: unknown): void {
|
||||
readString(player, 'player_name', 'PlayerName', 'display_name', 'displayName', 'nickname', 'username') ||
|
||||
(playerId === loggedInUserId.value ? loggedInUserName.value : '')
|
||||
const ready = readBoolean(player, 'ready', 'Ready') ?? false
|
||||
const missingSuit = readString(player, 'missing_suit', 'MissingSuit') || null
|
||||
const missingSuit = readMissingSuit(player)
|
||||
|
||||
playerMap.set(playerId, {
|
||||
roomPlayer: {
|
||||
@@ -705,7 +719,7 @@ function handleRoomInfoResponse(message: unknown): void {
|
||||
readNumber(player, 'index', 'Index', 'seat_index', 'seatIndex') ??
|
||||
fallbackIndex
|
||||
const displayName = existing?.gamePlayer.displayName || (playerId === loggedInUserId.value ? loggedInUserName.value : '')
|
||||
const missingSuit = readString(player, 'missing_suit', 'MissingSuit') || existing?.gamePlayer.missingSuit || null
|
||||
const missingSuit = readMissingSuit(player) || existing?.gamePlayer.missingSuit || null
|
||||
const handCount = readNumber(player, 'hand_count', 'handCount') ?? 0
|
||||
const outTiles = normalizeTiles(player.out_tiles ?? player.outTiles)
|
||||
const melds = normalizeMelds(
|
||||
@@ -749,9 +763,12 @@ function handleRoomInfoResponse(message: unknown): void {
|
||||
if (loggedInUserId.value && playerMap.has(loggedInUserId.value)) {
|
||||
const current = playerMap.get(loggedInUserId.value)
|
||||
if (current) {
|
||||
const selfMissingSuit = readMissingSuit(playerView)
|
||||
current.roomPlayer.hand = privateHand.map((tile) => tileToText(tile))
|
||||
current.roomPlayer.missingSuit = selfMissingSuit || current.roomPlayer.missingSuit
|
||||
current.gamePlayer.handTiles = privateHand
|
||||
current.gamePlayer.handCount = privateHand.length
|
||||
current.gamePlayer.missingSuit = selfMissingSuit || current.gamePlayer.missingSuit
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1030,26 +1047,11 @@ const seatDecor = computed<Record<SeatKey, SeatPlayerCardModel>>(() => {
|
||||
return result
|
||||
})
|
||||
|
||||
const floatingMissingSuit = computed(() => {
|
||||
const suitMap: Record<string, string> = {
|
||||
万: wanIcon,
|
||||
筒: tongIcon,
|
||||
条: tiaoIcon,
|
||||
}
|
||||
|
||||
const topLabel = seatDecor.value.top?.missingSuitLabel ?? ''
|
||||
const leftLabel = seatDecor.value.left?.missingSuitLabel ?? ''
|
||||
const rightLabel = seatDecor.value.right?.missingSuitLabel ?? ''
|
||||
|
||||
return {
|
||||
top: suitMap[topLabel] ?? '',
|
||||
left: suitMap[leftLabel] ?? '',
|
||||
right: suitMap[rightLabel] ?? '',
|
||||
}
|
||||
})
|
||||
|
||||
function missingSuitLabel(value: string | null | undefined): string {
|
||||
const suitMap: Record<string, string> = {
|
||||
w: '万',
|
||||
t: '筒',
|
||||
b: '条',
|
||||
wan: '万',
|
||||
tong: '筒',
|
||||
tiao: '条',
|
||||
@@ -1058,7 +1060,9 @@ function missingSuitLabel(value: string | null | undefined): string {
|
||||
if (!value) {
|
||||
return ''
|
||||
}
|
||||
return suitMap[value] ?? value
|
||||
|
||||
const normalized = value.trim().toLowerCase()
|
||||
return suitMap[normalized] ?? value
|
||||
}
|
||||
|
||||
function toggleMenu(): void {
|
||||
@@ -1825,18 +1829,18 @@ onBeforeUnmount(() => {
|
||||
<span v-if="wallSeats.left.hasHu" class="wall-hu-flag">胡</span>
|
||||
</div>
|
||||
|
||||
<div class="floating-status top">
|
||||
<img v-if="floatingMissingSuit.top" :src="floatingMissingSuit.top" alt=""/>
|
||||
<span>{{ seatDecor.top.missingSuitLabel }}</span>
|
||||
</div>
|
||||
<div class="floating-status left">
|
||||
<img v-if="floatingMissingSuit.left" :src="floatingMissingSuit.left" alt=""/>
|
||||
<span>{{ seatDecor.left.missingSuitLabel }}</span>
|
||||
</div>
|
||||
<div class="floating-status right">
|
||||
<img v-if="floatingMissingSuit.right" :src="floatingMissingSuit.right" alt=""/>
|
||||
<span>{{ seatDecor.right.missingSuitLabel }}</span>
|
||||
</div>
|
||||
<!-- <div class="floating-status top">-->
|
||||
<!-- <img v-if="floatingMissingSuit.top" :src="floatingMissingSuit.top" alt=""/>-->
|
||||
<!-- <span>{{ seatDecor.top.missingSuitLabel }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="floating-status left">-->
|
||||
<!-- <img v-if="floatingMissingSuit.left" :src="floatingMissingSuit.left" alt=""/>-->
|
||||
<!-- <span>{{ seatDecor.left.missingSuitLabel }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="floating-status right">-->
|
||||
<!-- <img v-if="floatingMissingSuit.right" :src="floatingMissingSuit.right" alt=""/>-->
|
||||
<!-- <span>{{ seatDecor.right.missingSuitLabel }}</span>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<WindSquare class="center-wind-square" :seat-winds="seatWinds"/>
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export function registerHandler(type: string, handler: Handler) {
|
||||
// 初始化监听
|
||||
export function initWsHandler() {
|
||||
wsClient.onMessage((msg) => {
|
||||
console.log('[WS] 收到消息:', msg)
|
||||
const handlers = handlerMap[msg.type]
|
||||
|
||||
if (handlers && handlers.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user