From ceba41fb08356dfaed18bf958b3c15a3dce1e344 Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Tue, 24 Mar 2026 16:26:13 +0800 Subject: [PATCH] ``` style(global): update background gradients and visual styling - Replace radial gradient with combined radial and linear gradients - Update color schemes with warmer tones and improved transparency - Adjust border colors and add subtle glow effects - Increase blur intensity for better glassmorphism effect style(game): enhance seat player card design - Add avatar panel container for better layout structure - Implement dealer mark positioning with absolute placement - Add missing suit icons with computed property mapping - Replace text-based missing marks with image icons when available - Improve visual hierarchy and spacing between elements refactor(game): add computed property for dynamic suit icon selection - Import suit icon assets (wan, tong, tiao) - Create computed property to map suit labels to corresponding icons - Handle fallback to text display when no icon is available ``` --- src/assets/styles/global.css | 101 ++- src/components/game/SeatPlayerCard.vue | 32 +- src/views/ChengduGamePage.vue | 1101 +++++++++++++++++++----- 3 files changed, 966 insertions(+), 268 deletions(-) diff --git a/src/assets/styles/global.css b/src/assets/styles/global.css index 81e0ee8..95cadbd 100644 --- a/src/assets/styles/global.css +++ b/src/assets/styles/global.css @@ -23,7 +23,9 @@ body, body { margin: 0; min-width: 320px; - background: radial-gradient(circle at 12% 12%, #254935 0%, #11251c 45%, #0a1411 100%); + background: + radial-gradient(circle at top, rgba(219, 171, 91, 0.16), transparent 22%), + linear-gradient(180deg, #442621 0%, #24110e 100%); } #app { @@ -54,10 +56,12 @@ p { width: min(440px, 100%); padding: 28px 24px; border-radius: 16px; - border: 1px solid rgba(255, 255, 255, 0.16); - background: rgba(8, 27, 20, 0.82); - backdrop-filter: blur(8px); - box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35); + border: 1px solid rgba(246, 212, 139, 0.18); + background: + linear-gradient(180deg, rgba(62, 33, 26, 0.96), rgba(26, 14, 11, 0.96)), + radial-gradient(circle at top, rgba(255, 214, 134, 0.08), transparent 40%); + backdrop-filter: blur(10px); + box-shadow: 0 18px 40px rgba(0, 0, 0, 0.42); } .auth-card h1 { @@ -204,8 +208,10 @@ button:disabled { gap: 12px; padding: 18px; border-radius: 14px; - border: 1px solid rgba(255, 255, 255, 0.15); - background: linear-gradient(130deg, rgba(22, 57, 43, 0.9), rgba(10, 30, 22, 0.92)); + border: 1px solid rgba(244, 210, 140, 0.14); + background: + linear-gradient(180deg, rgba(62, 33, 26, 0.94), rgba(30, 15, 12, 0.92)), + radial-gradient(circle at top, rgba(255, 214, 134, 0.08), transparent 42%); } .hall-topbar { @@ -289,8 +295,8 @@ button:disabled { .panel { padding: 18px; border-radius: 14px; - border: 1px solid rgba(255, 255, 255, 0.12); - background: rgba(10, 30, 22, 0.85); + border: 1px solid rgba(244, 210, 140, 0.12); + background: rgba(40, 21, 17, 0.8); } .panel h2 { @@ -762,16 +768,25 @@ button:disabled { display: flex; align-items: center; gap: 10px; - min-width: 148px; - padding: 8px 12px; - border-radius: 14px; - border: 1px solid rgba(244, 222, 163, 0.24); - background: rgba(8, 27, 20, 0.72); - box-shadow: 0 12px 28px rgba(0, 0, 0, 0.2); + min-width: 154px; + padding: 9px 12px; + border-radius: 16px; + border: 1px solid rgba(248, 226, 173, 0.24); + background: + linear-gradient(180deg, rgba(43, 52, 73, 0.84), rgba(17, 22, 34, 0.82)), + radial-gradient(circle at top, rgba(255, 255, 255, 0.08), transparent 40%); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.08), + 0 12px 28px rgba(0, 0, 0, 0.24); +} + +.avatar-panel { + position: relative; + flex: 0 0 auto; } .player-badge.seat-top { - top: 76px; + top: 28px; left: 50%; transform: translateX(-50%); } @@ -783,7 +798,7 @@ button:disabled { } .player-badge.seat-bottom { - bottom: 90px; + bottom: 136px; left: 50%; transform: translateX(-50%); } @@ -805,22 +820,29 @@ button:disabled { .avatar-card { display: grid; place-items: center; - width: 42px; - height: 42px; - border-radius: 12px; - background: linear-gradient(145deg, #ecd995, #d3b767); - color: #1c2d23; + width: 48px; + height: 48px; + border-radius: 10px; + border: 1px solid rgba(255, 248, 215, 0.32); + background: + linear-gradient(145deg, #b3e79c, #4eaf4a 46%, #2f7e28 100%); + color: #f7fff7; font-weight: 800; + box-shadow: + inset 0 2px 4px rgba(255, 255, 255, 0.18), + 0 6px 14px rgba(0, 0, 0, 0.22); } .player-meta p { font-size: 14px; font-weight: 700; + color: #eef5ff; } .player-meta strong { - font-size: 13px; - color: #f7e4b0; + font-size: 15px; + color: #ffd85c; + text-shadow: 0 0 10px rgba(255, 216, 92, 0.2); } .dealer-mark, @@ -828,22 +850,39 @@ button:disabled { display: inline-flex; align-items: center; justify-content: center; - min-width: 24px; - height: 24px; - padding: 0 6px; + min-width: 28px; + height: 28px; border-radius: 999px; font-size: 12px; } .dealer-mark { - background: rgba(236, 188, 84, 0.88); - color: #1c2d23; + position: absolute; + right: -8px; + bottom: -6px; + background: linear-gradient(180deg, #ffe38a 0%, #f1b92e 100%); + color: #5f3200; + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.18); } .missing-mark { margin-left: auto; - background: rgba(255, 255, 255, 0.08); - color: #d6eadf; + width: 34px; + height: 34px; + padding: 0; + overflow: hidden; + background: linear-gradient(180deg, rgba(114, 219, 149, 0.2) 0%, rgba(21, 148, 88, 0.34) 100%); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.16); +} + +.missing-mark img { + width: 22px; + height: 22px; + object-fit: contain; +} + +.missing-mark span { + color: #effff5; } .wall { diff --git a/src/components/game/SeatPlayerCard.vue b/src/components/game/SeatPlayerCard.vue index 8544e36..003b993 100644 --- a/src/components/game/SeatPlayerCard.vue +++ b/src/components/game/SeatPlayerCard.vue @@ -1,10 +1,27 @@ diff --git a/src/views/ChengduGamePage.vue b/src/views/ChengduGamePage.vue index de7ff56..cf8dd84 100644 --- a/src/views/ChengduGamePage.vue +++ b/src/views/ChengduGamePage.vue @@ -2,6 +2,9 @@ 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 topBackImage from '../assets/images/tiles/top/tbgs_2.png' import rightBackImage from '../assets/images/tiles/right/tbgs_1.png' import bottomBackImage from '../assets/images/tiles/bottom/tdbgs_4.png' @@ -86,10 +89,6 @@ const formattedClock = computed(() => { }) }) -const statusRibbon = computed(() => { - return roomState.value.game?.rule?.name || currentPhaseText.value -}) - const wallBacks = computed>(() => { const wallSize = roomState.value.game?.state?.wall.length ?? 0 const perSide = Math.max(6, Math.ceil((wallSize || 48) / 4 / 2)) @@ -138,42 +137,24 @@ const seatDecor = computed>(() => { dealer: seat.player.index === dealerIndex, isTurn: seat.isTurn, isOnline: true, - missingSuitLabel: defaultMissingSuitLabel, + missingSuitLabel: missingSuitLabel(seat.player.missingSuit), } } return result }) -const seatMarkers = computed(() => { - const seatTitleMap: Record = { - top: '上家', - right: '右家', - bottom: '本家', - left: '左家', - } - - return seatViews.value.map((seat) => ({ - key: seat.key, - occupied: Boolean(seat.player), - isSelf: seat.isSelf, - isTurn: seat.isTurn, - label: seat.player ? seatTitleMap[seat.key] : '空位', - subLabel: seat.player ? `座位 ${seat.player.index}` : '', - })) -}) - const centerTimer = computed(() => { const wallLeft = roomState.value.game?.state?.wall.length if (typeof wallLeft === 'number' && Number.isFinite(wallLeft)) { - return `余牌 ${wallLeft}` + return String(wallLeft).padStart(2, '0') } - return roomState.value.playerCount > 0 - ? `${roomState.value.playerCount}/${roomState.value.maxPlayers} 人` - : '等待中' + return String(roomState.value.playerCount).padStart(2, '0') }) +const selectedTileText = computed(() => selectedTile.value ?? '未选择') + const pendingClaimText = computed(() => { const claim = roomState.value.game?.state?.pendingClaim if (!claim) { @@ -183,12 +164,24 @@ const pendingClaimText = computed(() => { try { return JSON.stringify(claim) } catch { - return '存在响应窗口' + return '有待响应动作' } }) -const selectedTileText = computed(() => { - return selectedTile.value ?? '未选择' +const rightMessages = computed(() => wsMessages.value.slice(-16).reverse()) + +const floatingMissingSuit = computed(() => { + const suitMap: Record = { + 万: wanIcon, + 筒: tongIcon, + 条: tiaoIcon, + } + + return { + top: suitMap[seatDecor.value.top.missingSuitLabel] ?? '', + left: suitMap[seatDecor.value.left.missingSuitLabel] ?? '', + right: suitMap[seatDecor.value.right.missingSuitLabel] ?? '', + } }) function missingSuitLabel(value: string | null | undefined): string { @@ -216,6 +209,16 @@ function getBackImage(seat: SeatKey): string { return imageMap[seat] } +function actionTheme(type: string): 'gold' | 'jade' | 'blue' { + if (type === 'hu' || type === 'gang') { + return 'gold' + } + if (type === 'pass') { + return 'jade' + } + return 'blue' +} + onMounted(() => { clockTimer = window.setInterval(() => { now.value = Date.now() @@ -231,249 +234,881 @@ onBeforeUnmount(() => {