Skip to content

Commit ffaae5b

Browse files
committed
refractor
1 parent 9c088f0 commit ffaae5b

2 files changed

Lines changed: 372 additions & 259 deletions

File tree

src/App.vue

Lines changed: 21 additions & 259 deletions
Original file line numberDiff line numberDiff line change
@@ -5,68 +5,22 @@
55
<GameSetup v-if="showSetup" @start="handleGameSetupStart" />
66

77
<!-- 棋盘主面板 -->
8-
<div class="game-panel">
9-
<div class="board-frame" :class="{ 'coordinates-outside': coordinateLabelMode === 'outside' }">
10-
<div class="board-grid" ref="boardGridRef">
11-
<template v-for="displayRow in 8" :key="`rank-${displayRow}`">
12-
<button v-for="displayCol in 8" :key="`${displayRow}-${displayCol}`" type="button" class="board-square"
13-
:class="{ 'draggable-piece': board[getActualRow(displayRow - 1)]?.[getActualCol(displayCol - 1)]?.color === currentTurn }"
14-
@mousedown="handleMouseDown(getActualRow(displayRow - 1), getActualCol(displayCol - 1), $event)"
15-
@mouseenter="hoverSquare = { row: getActualRow(displayRow - 1), col: getActualCol(displayCol - 1) }"
16-
@mouseleave="hoverSquare = null"
17-
:aria-label="getSquareLabel(getActualRow(displayRow - 1), getActualCol(displayCol - 1))">
18-
19-
<img class="square-background base" draggable="false"
20-
:src="isWhiteSquare(getActualRow(displayRow - 1), getActualCol(displayCol - 1)) ? './texture/board/board_white.png' : './texture/board/board_black.png'"
21-
alt="" />
22-
23-
<img v-if="getOverlayTexture(board, selectedSquare, possibleMoves, isDragging, hoverSquare, getActualRow(displayRow - 1), getActualCol(displayCol - 1))"
24-
class="square-background overlay" draggable="false"
25-
:src="getOverlayTexture(board, selectedSquare, possibleMoves, isDragging, hoverSquare, getActualRow(displayRow - 1), getActualCol(displayCol - 1))!" alt="" />
26-
27-
<img v-if="board[getActualRow(displayRow - 1)]?.[getActualCol(displayCol - 1)]" class="piece"
28-
draggable="false" :class="{
29-
'dragging-hidden': isDragging && dragStartSquare?.row === getActualRow(displayRow - 1) && dragStartSquare?.col === getActualCol(displayCol - 1)
30-
}" :src="getPieceImage(board[getActualRow(displayRow - 1)]?.[getActualCol(displayCol - 1)]!, board, isDraw, hasResigned, timeoutWinner)"
31-
:alt="board[getActualRow(displayRow - 1)]?.[getActualCol(displayCol - 1)]!.type" />
32-
33-
<div v-if="coordinateLabelMode === 'inside' && displayCol === 1" class="coordinate-label rank"
34-
:class="isWhiteSquare(getActualRow(displayRow - 1), getActualCol(displayCol - 1)) ? 'text-black' : 'text-white'">
35-
{{ 8 - getActualRow(displayRow - 1) }}
36-
</div>
37-
38-
<div v-if="coordinateLabelMode === 'inside' && displayRow === 8" class="coordinate-label file"
39-
:class="isWhiteSquare(getActualRow(displayRow - 1), getActualCol(displayCol - 1)) ? 'text-black' : 'text-white'">
40-
{{ String.fromCharCode(97 + getActualCol(displayCol - 1)) }}
41-
</div>
42-
</button>
43-
</template>
44-
<div v-if="promotionPending" class="promotion-overlay" @click="cancelPromotion"></div>
45-
<Promotion v-if="promotionPending" :color="promotionPending.color" :style="promotionStyle"
46-
@select="applyPromotion" />
47-
</div>
48-
49-
<div v-if="coordinateLabelMode === 'outside'" class="coordinates-outside-layer">
50-
<div class="coordinate-bottom-row">
51-
<span v-for="displayCol in 8" :key="`file-${displayCol}`" class="coordinate-label outer-file"
52-
:style="{ left: `${(displayCol - 0.5) * 12.5}%` }">
53-
{{ getDisplayedFile(displayCol) }}
54-
</span>
55-
</div>
56-
57-
<div class="coordinate-side-col">
58-
<span v-for="displayRow in 8" :key="`rank-${displayRow}`" class="coordinate-label outer-rank"
59-
:style="{ top: `${(displayRow - 0.5) * 12.5}%` }">
60-
{{ getDisplayedRank(displayRow) }}
61-
</span>
62-
</div>
63-
</div>
64-
</div>
65-
66-
<img v-if="isDragging && dragStartSquare" class="floating-piece no-select" draggable="false"
67-
:src="getPieceImage(board[dragStartSquare.row]?.[dragStartSquare.col]!, board, isDraw, hasResigned, timeoutWinner)"
68-
:style="{ left: mousePos.x + 'px', top: mousePos.y + 'px' }" alt="" />
69-
</div>
8+
<BoardPanel :board="board" :current-turn="currentTurn" :selected-square="selectedSquare"
9+
:possible-moves="possibleMoves" :is-dragging="isDragging" :drag-start-square="dragStartSquare"
10+
:hover-square="hoverSquare" :mouse-pos="mousePos" :is-mouse-down="isMouseDown"
11+
:promotion-pending="promotionPending" :promotion-style="promotionStyle" :is-draw="isDraw"
12+
:has-resigned="hasResigned" :timeout-winner="timeoutWinner" :coordinate-label-mode="coordinateLabelMode"
13+
:is-flipped="isFlipped"
14+
:get-overlay-texture="getOverlayTexture"
15+
:get-piece-image="getPieceImage"
16+
:get-square-label="getSquareLabel"
17+
:is-white-square="isWhiteSquare"
18+
@update:piece-scale="(val: number) => pieceScale = val"
19+
@square-mousedown="(row: number, col: number, event: MouseEvent) => handleMouseDown(row, col, event)"
20+
@square-mouseenter="(row: number, col: number) => hoverSquare = { row, col }"
21+
@square-mouseleave="hoverSquare = null"
22+
@cancel-promotion="cancelPromotion"
23+
@apply-promotion="(piece: string) => applyPromotion(piece)" />
7024

7125
<!-- 侧边栏 -->
7226
<Sidebar :is-clock-enabled="isClockEnabled" :move-history="moveHistory" :current-turn="currentTurn"
@@ -92,11 +46,11 @@
9246
</template>
9347

9448
<script setup lang="ts">
95-
import { onMounted, onUnmounted, ref } from 'vue'
96-
import Promotion from './components/Promotion.vue'
49+
import { onUnmounted, ref } from 'vue'
9750
import Sidebar from './components/Sidebar.vue'
9851
import SettingsModal from './components/SettingsModal.vue'
9952
import GameSetup from './components/GameSetup.vue'
53+
import BoardPanel from './components/BoardPanel.vue'
10054
import { useSettings } from './composables/useSettings'
10155
import { useBoardDisplay } from './composables/useBoardDisplay'
10256
import { useGameState } from './composables/useGameState'
@@ -110,19 +64,15 @@ const showSettingsModal = ref(false)
11064
// ---- 棋盘显示 ----
11165
const {
11266
isFlipped,
113-
getActualRow,
114-
getActualCol,
115-
getDisplayedFile,
116-
getDisplayedRank,
11767
getOverlayTexture,
11868
getPieceImage,
119-
pieceScale,
120-
boardGridRef,
12169
getSquareLabel,
12270
isWhiteSquare,
123-
updatePieceScale,
12471
} = useBoardDisplay()
12572
73+
// ---- pieceScale(由 BoardPanel 通过事件更新) ----
74+
const pieceScale = ref(1.5)
75+
12676
// ---- 游戏核心状态(含棋钟、拖拽、音效、走棋) ----
12777
const game = useGameState(isSoundEnabled, isFlipped)
12878
@@ -166,10 +116,6 @@ const {
166116
} = game
167117
168118
// ---- 生命周期 ----
169-
onMounted(() => {
170-
updatePieceScale()
171-
})
172-
173119
onUnmounted(() => {
174120
stopClock()
175121
})
@@ -194,195 +140,11 @@ onUnmounted(() => {
194140
cursor: auto;
195141
}
196142
197-
.game-panel {
198-
width: 100%;
199-
max-width: 80vmin;
200-
display: flex;
201-
flex-direction: column;
202-
justify-content: center;
203-
align-items: center;
204-
box-sizing: border-box;
205-
}
206-
207143
.game-container.global-dragging,
208144
.game-container.global-dragging * {
209145
cursor: grabbing !important;
210146
}
211147
212-
.board-frame {
213-
position: relative;
214-
width: 100%;
215-
aspect-ratio: 1 / 1;
216-
max-width: calc(100vh - 120px);
217-
width: 80vmin;
218-
margin: 0 auto;
219-
box-sizing: border-box;
220-
overflow: visible;
221-
}
222-
223-
.board-frame.coordinates-outside {
224-
padding: 0;
225-
}
226-
227-
.board-grid {
228-
display: grid;
229-
grid-template-columns: repeat(8, minmax(0, 1fr));
230-
grid-template-rows: repeat(8, minmax(0, 1fr));
231-
width: 100%;
232-
height: 100%;
233-
gap: 0;
234-
position: relative;
235-
}
236-
237-
.board-square {
238-
position: relative;
239-
overflow: visible;
240-
border: none;
241-
padding: 0;
242-
background: transparent;
243-
cursor: default;
244-
}
245-
246-
.board-square:focus,
247-
.board-square:focus-visible,
248-
.board-square:active {
249-
outline: none !important;
250-
box-shadow: none !important;
251-
}
252-
253-
.board-square.draggable-piece {
254-
cursor: grab;
255-
}
256-
257-
.board-square:focus-visible {
258-
outline: 2px solid #ffd33d;
259-
outline-offset: -2px;
260-
z-index: 10;
261-
}
262-
263-
.square-background,
264-
.piece {
265-
display: block;
266-
image-rendering: crisp-edges;
267-
image-rendering: pixelated;
268-
}
269-
270-
.square-background.base {
271-
width: 100%;
272-
height: 100%;
273-
object-fit: cover;
274-
}
275-
276-
.square-background.overlay {
277-
position: absolute;
278-
top: 0;
279-
left: 0;
280-
width: 100%;
281-
height: 100%;
282-
z-index: 1;
283-
pointer-events: none;
284-
}
285-
286-
.piece {
287-
position: absolute;
288-
left: 50%;
289-
bottom: 15%;
290-
transform: translateX(-50%) scale(var(--piece-scale));
291-
transform-origin: bottom center;
292-
pointer-events: auto;
293-
cursor: grab;
294-
z-index: 10;
295-
transition: opacity 0.1s ease;
296-
}
297-
298-
.piece.dragging-hidden {
299-
opacity: 0;
300-
}
301-
302-
.floating-piece {
303-
position: fixed;
304-
pointer-events: none;
305-
z-index: 9999;
306-
transform: translate(-50%, -50%) scale(var(--piece-scale));
307-
image-rendering: crisp-edges;
308-
image-rendering: pixelated;
309-
}
310-
311-
.coordinate-label {
312-
position: absolute;
313-
font-family: 'Unifont', monospace;
314-
font-size: 0.75rem;
315-
line-height: 1;
316-
font-weight: bold;
317-
pointer-events: none;
318-
z-index: 5;
319-
user-select: none;
320-
}
321-
322-
.coordinates-outside-layer {
323-
position: absolute;
324-
inset: 0;
325-
pointer-events: none;
326-
}
327-
328-
.coordinate-bottom-row {
329-
position: absolute;
330-
left: 0;
331-
right: 0;
332-
bottom: -1.25rem;
333-
height: 1rem;
334-
}
335-
336-
.coordinate-side-col {
337-
position: absolute;
338-
top: 0;
339-
bottom: 0;
340-
left: -1.25rem;
341-
width: 1rem;
342-
}
343-
344-
.coordinate-label.outer-file {
345-
position: absolute;
346-
transform: translateX(-50%);
347-
font-size: 0.85rem;
348-
color: #333;
349-
text-align: center;
350-
}
351-
352-
.coordinate-label.outer-rank {
353-
position: absolute;
354-
transform: translateY(-50%);
355-
font-size: 0.85rem;
356-
color: #333;
357-
text-align: center;
358-
width: 100%;
359-
}
360-
361-
.coordinate-label.rank {
362-
top: 2px;
363-
left: 4px;
364-
}
365-
366-
.coordinate-label.file {
367-
bottom: 3px;
368-
right: 4px;
369-
}
370-
371-
.coordinate-label.text-black {
372-
color: #484A4B;
373-
}
374-
375-
.coordinate-label.text-white {
376-
color: #F3F9FC;
377-
}
378-
379-
.promotion-overlay {
380-
position: absolute;
381-
inset: 0;
382-
background: rgba(0, 0, 0, 0.45);
383-
z-index: 50;
384-
}
385-
386148
/* 右上角固定设置按钮 */
387149
.settings-fab {
388150
position: fixed;

0 commit comments

Comments
 (0)