Compare commits
6 Commits
21a348663b
...
ba6925afd9
Author | SHA1 | Date | |
---|---|---|---|
ba6925afd9 | |||
35b805f53f | |||
a158249f23 | |||
aab9b43fe0 | |||
c389bb7d95 | |||
c2a44ba7c7 |
403
src/App.vue
403
src/App.vue
@ -76,25 +76,57 @@
|
|||||||
<!-- 胜利弹窗 -->
|
<!-- 胜利弹窗 -->
|
||||||
<div v-if="winner" class="victory-modal">
|
<div v-if="winner" class="victory-modal">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<h2>🎉 游戏结束 🎉</h2>
|
<div class="victory-header">
|
||||||
<p v-if="winner === 'timeout'">
|
<div class="victory-emoji">
|
||||||
{{ getPlayerName(currentPlayer === 1 ? 2 : 1) }}胜利!<br />对手超时三次被判负
|
<template v-if="gameMode === 'pve'">
|
||||||
</p>
|
<template v-if="winner === (isPlayerBlack ? 1 : 2)">
|
||||||
<p v-else>
|
<!-- 玩家胜利 -->
|
||||||
{{ getPlayerName(winner) }}胜利!<br />
|
🎉
|
||||||
第 {{ moveCount }} 手获胜
|
</template>
|
||||||
</p>
|
<template v-else>
|
||||||
<GameStatistics v-if="gameMode === 'pve'" :stats="statistics" />
|
<!-- 玩家失败 -->
|
||||||
<p
|
😢
|
||||||
v-if="gameMode === 'pve' && winner !== (isPlayerBlack ? 1 : 2)"
|
</template>
|
||||||
class="taunt-message"
|
</template>
|
||||||
>
|
<template v-else>
|
||||||
{{ getTauntMessage() }}
|
<!-- PVP模式 -->
|
||||||
</p>
|
🎉
|
||||||
<div class="modal-buttons">
|
</template>
|
||||||
<button @click="closeModal" class="modal-button secondary">查看棋盘</button>
|
</div>
|
||||||
<button @click="playAgain" class="modal-button primary">再来一局</button>
|
<h2>游戏结束</h2>
|
||||||
<button @click="exitGame" class="modal-button">退出游戏</button>
|
</div>
|
||||||
|
|
||||||
|
<div class="victory-info">
|
||||||
|
<div class="victory-result">
|
||||||
|
<p v-if="winner === 'timeout'">
|
||||||
|
{{ getPlayerName(currentPlayer === 1 ? 2 : 1) }}胜利!<br />对手超时三次被判负
|
||||||
|
</p>
|
||||||
|
<p v-else>
|
||||||
|
{{ getPlayerName(winner) }}胜利!<br />
|
||||||
|
第 {{ moveCount }} 手获胜
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<div class="victory-stats">
|
||||||
|
<GameStatistics v-if="gameMode === 'pve'" :stats="statistics" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="victory-footer">
|
||||||
|
<p
|
||||||
|
v-if="gameMode === 'pve' && winner !== (isPlayerBlack ? 1 : 2)"
|
||||||
|
class="taunt-message"
|
||||||
|
>
|
||||||
|
{{ getTauntMessage() }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="modal-buttons">
|
||||||
|
<button @click="closeModal" class="modal-button secondary">查看棋盘</button>
|
||||||
|
<button @click="playAgain" class="modal-button primary">再来一局</button>
|
||||||
|
<button @click="exitGame" class="modal-button exit">退出游戏</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -197,8 +229,7 @@ export default {
|
|||||||
winningStreaks: {
|
winningStreaks: {
|
||||||
current: 0,
|
current: 0,
|
||||||
max: 0,
|
max: 0,
|
||||||
player: 0,
|
maxLose: 0,
|
||||||
computer: 0,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
winningPositions: null, // 保存获胜时的高亮状态
|
winningPositions: null, // 保存获胜时的高亮状态
|
||||||
@ -242,7 +273,15 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleClick(row, col) {
|
handleClick(row, col) {
|
||||||
if (this.board[row][col].player !== 0) return
|
if (
|
||||||
|
!this.gameStarted ||
|
||||||
|
this.board[row][col].player !== 0 ||
|
||||||
|
this.winner ||
|
||||||
|
this.gameEnded || // 增加游戏结束的检查
|
||||||
|
(this.gameMode === 'pve' && this.isComputerTurn)
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.stopTimer()
|
this.stopTimer()
|
||||||
|
|
||||||
@ -313,7 +352,6 @@ export default {
|
|||||||
|
|
||||||
handleTimeout(player) {
|
handleTimeout(player) {
|
||||||
this.stopTimer()
|
this.stopTimer()
|
||||||
console.log('超时玩家:', player === 1 ? '黑方' : '白方')
|
|
||||||
|
|
||||||
// 记录当前状态
|
// 记录当前状态
|
||||||
const isPlayerTurn =
|
const isPlayerTurn =
|
||||||
@ -406,7 +444,7 @@ export default {
|
|||||||
|
|
||||||
// 查获胜条件
|
// 查获胜条件
|
||||||
checkWinner(row, col) {
|
checkWinner(row, col) {
|
||||||
const player = this.board[row][col].player
|
// const player = this.board[row][col].player
|
||||||
let hasFiveInRow = false
|
let hasFiveInRow = false
|
||||||
let winningPositions = []
|
let winningPositions = []
|
||||||
|
|
||||||
@ -457,7 +495,6 @@ export default {
|
|||||||
if (!this.isValidPosition(newRow, newCol)) break
|
if (!this.isValidPosition(newRow, newCol)) break
|
||||||
const nextCell = this.board[newRow][newCol]
|
const nextCell = this.board[newRow][newCol]
|
||||||
if (nextCell.player !== playerType) {
|
if (nextCell.player !== playerType) {
|
||||||
console.log('遇到非己方棋子:', newRow, newCol)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
pieces.push([newRow, newCol])
|
pieces.push([newRow, newCol])
|
||||||
@ -471,20 +508,16 @@ export default {
|
|||||||
if (!this.isValidPosition(newRow, newCol)) break
|
if (!this.isValidPosition(newRow, newCol)) break
|
||||||
const nextCell = this.board[newRow][newCol]
|
const nextCell = this.board[newRow][newCol]
|
||||||
if (nextCell.player !== playerType) {
|
if (nextCell.player !== playerType) {
|
||||||
console.log('遇到非己方棋子:', newRow, newCol)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
pieces.unshift([newRow, newCol])
|
pieces.unshift([newRow, newCol])
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('找到的棋子:', pieces)
|
|
||||||
|
|
||||||
// 严格检查是否形成五子连珠
|
// 严格检查是否形成五子连珠
|
||||||
const isWin = count === 5 && pieces.length === 5
|
const isWin = count === 5 && pieces.length === 5
|
||||||
|
|
||||||
if (isWin) {
|
if (isWin) {
|
||||||
console.log('确认五子连珠!玩家:', playerType, '位置:', pieces)
|
|
||||||
this.playWinningAnimation()
|
this.playWinningAnimation()
|
||||||
return {
|
return {
|
||||||
isWin: true,
|
isWin: true,
|
||||||
@ -520,14 +553,12 @@ export default {
|
|||||||
|
|
||||||
// 检查相邻棋子的距离是否严格为1,且方向一致
|
// 检查相邻棋子的距离是否严格为1,且方向一致
|
||||||
if (curr[0] - prev[0] !== dx || curr[1] - prev[1] !== dy) {
|
if (curr[0] - prev[0] !== dx || curr[1] - prev[1] !== dy) {
|
||||||
console.log('棋子不连续或方向不一致:', curr, prev)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否属于同一玩家
|
// 检查是否属于同一玩家
|
||||||
const currPiece = this.board[curr[0]][curr[1]]
|
const currPiece = this.board[curr[0]][curr[1]]
|
||||||
if (this.isPlayerPiece(currPiece) !== isHuman) {
|
if (this.isPlayerPiece(currPiece) !== isHuman) {
|
||||||
console.log('棋子属于不同玩家:', curr)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -536,13 +567,6 @@ export default {
|
|||||||
|
|
||||||
// 验证获胜棋子
|
// 验证获胜棋子
|
||||||
validateWinningPieces(pieces, player) {
|
validateWinningPieces(pieces, player) {
|
||||||
console.log('验证获胜棋子:', pieces, '期望玩家:', player)
|
|
||||||
const validation = pieces.map(([row, col]) => ({
|
|
||||||
position: [row, col],
|
|
||||||
valid: this.isValidPosition(row, col),
|
|
||||||
piece: this.board[row][col],
|
|
||||||
}))
|
|
||||||
console.log('验证详情:', validation)
|
|
||||||
return pieces.every(([row, col]) => {
|
return pieces.every(([row, col]) => {
|
||||||
return this.isValidPosition(row, col) && this.board[row][col].player === player
|
return this.isValidPosition(row, col) && this.board[row][col].player === player
|
||||||
})
|
})
|
||||||
@ -1200,31 +1224,31 @@ export default {
|
|||||||
if (this.currentPlayer === (this.isPlayerBlack ? 1 : 2)) {
|
if (this.currentPlayer === (this.isPlayerBlack ? 1 : 2)) {
|
||||||
stats.computerWins++
|
stats.computerWins++
|
||||||
stats.winningStreaks.current = Math.min(0, stats.winningStreaks.current) - 1
|
stats.winningStreaks.current = Math.min(0, stats.winningStreaks.current) - 1
|
||||||
|
stats.winningStreaks.maxLose = Math.max(
|
||||||
|
stats.winningStreaks.maxLose,
|
||||||
|
Math.abs(stats.winningStreaks.current),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
stats.playerWins++
|
stats.playerWins++
|
||||||
stats.winningStreaks.current = Math.max(0, stats.winningStreaks.current) + 1
|
stats.winningStreaks.current = Math.max(0, stats.winningStreaks.current) + 1
|
||||||
|
stats.winningStreaks.max = Math.max(
|
||||||
|
stats.winningStreaks.max,
|
||||||
|
stats.winningStreaks.current,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else if (winner === (this.isPlayerBlack ? 1 : 2)) {
|
} else if (winner === (this.isPlayerBlack ? 1 : 2)) {
|
||||||
stats.playerWins++
|
stats.playerWins++
|
||||||
stats.winningStreaks.current = Math.max(0, stats.winningStreaks.current) + 1
|
stats.winningStreaks.current = Math.max(0, stats.winningStreaks.current) + 1
|
||||||
stats.winningStreaks.player = Math.max(
|
stats.winningStreaks.max = Math.max(stats.winningStreaks.max, stats.winningStreaks.current)
|
||||||
stats.winningStreaks.player,
|
|
||||||
stats.winningStreaks.current,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
stats.computerWins++
|
stats.computerWins++
|
||||||
stats.winningStreaks.current = Math.min(0, stats.winningStreaks.current) - 1
|
stats.winningStreaks.current = Math.min(0, stats.winningStreaks.current) - 1
|
||||||
stats.winningStreaks.computer = Math.max(
|
stats.winningStreaks.maxLose = Math.max(
|
||||||
stats.winningStreaks.computer,
|
stats.winningStreaks.maxLose,
|
||||||
Math.abs(stats.winningStreaks.current),
|
Math.abs(stats.winningStreaks.current),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.winningStreaks.max = Math.max(
|
|
||||||
stats.winningStreaks.player,
|
|
||||||
stats.winningStreaks.computer,
|
|
||||||
)
|
|
||||||
|
|
||||||
// 保存到本地存储
|
// 保存到本地存储
|
||||||
this.saveStatistics()
|
this.saveStatistics()
|
||||||
},
|
},
|
||||||
@ -1271,6 +1295,7 @@ export default {
|
|||||||
<style>
|
<style>
|
||||||
/* 基础样式 */
|
/* 基础样式 */
|
||||||
.container {
|
.container {
|
||||||
|
width: 100vw;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -1294,10 +1319,27 @@ export default {
|
|||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 40px;
|
padding: 30px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||||
animation: modalFadeIn 0.3s ease-out;
|
animation: modalFadeIn 0.3s ease-out;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600px;
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: linear-gradient(90deg, #4caf50, #45a049);
|
||||||
|
border-radius: 16px 16px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content h2 {
|
.modal-content h2 {
|
||||||
@ -1655,105 +1697,146 @@ export default {
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.victory-modal .modal-content {
|
.modal-content {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 40px;
|
padding: 30px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
text-align: center;
|
|
||||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||||
animation: modalFadeIn 0.3s ease-out;
|
animation: modalFadeIn 0.3s ease-out;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.victory-modal h2 {
|
.victory-header {
|
||||||
font-size: 28px;
|
text-align: center;
|
||||||
color: #2c3e50;
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.victory-emoji {
|
||||||
|
font-size: 48px;
|
||||||
|
animation: bounce 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.victory-header h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.victory-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.victory-result {
|
||||||
|
background: linear-gradient(145deg, #f8f9fa, #e9ecef);
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 12px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #2c3e50;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
height: 1px;
|
||||||
|
background: linear-gradient(90deg, transparent, #e0e0e0, transparent);
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.victory-stats {
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.taunt-message {
|
||||||
|
margin: 15px 0;
|
||||||
|
padding: 12px;
|
||||||
|
background: linear-gradient(145deg, #e53935, #d32f2f);
|
||||||
|
color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 4px 12px rgba(229, 57, 53, 0.2);
|
||||||
|
animation: slideIn 0.3s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-buttons {
|
.modal-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20px;
|
gap: 15px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 30px;
|
margin-top: 20px;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-button {
|
.modal-button {
|
||||||
padding: 12px 24px;
|
padding: 10px 20px;
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
background-color: #6c757d;
|
|
||||||
color: white;
|
color: white;
|
||||||
}
|
min-width: 100px;
|
||||||
|
font-weight: 500;
|
||||||
.modal-button.primary {
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
background-color: #4caf50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start-screen {
|
|
||||||
text-align: center;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
z-index: 10;
|
|
||||||
background: rgba(255, 255, 255, 0.95);
|
|
||||||
padding: 25px 40px;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
|
||||||
backdrop-filter: blur(4px);
|
|
||||||
min-width: 240px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start-button {
|
|
||||||
padding: 12px 36px;
|
|
||||||
font-size: 18px;
|
|
||||||
background: linear-gradient(145deg, #4caf50, #45a049);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.2);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start-button:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 6px 15px rgba(76, 175, 80, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.turn-info {
|
|
||||||
color: #2c3e50;
|
|
||||||
font-size: 15px;
|
|
||||||
margin-top: 5px;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-button.secondary {
|
.modal-button.secondary {
|
||||||
background-color: #6c757d;
|
background-color: #6c757d;
|
||||||
}
|
box-shadow: 0 2px 8px rgba(108, 117, 125, 0.2);
|
||||||
|
background: linear-gradient(145deg, #6c757d, #5a6268);
|
||||||
.modal-button.secondary:hover {
|
|
||||||
background-color: #5a6268;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-button.primary {
|
.modal-button.primary {
|
||||||
background-color: #4caf50;
|
background-color: #4caf50;
|
||||||
|
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.2);
|
||||||
|
background: linear-gradient(145deg, #4caf50, #45a049);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-button.primary:hover {
|
.modal-button.exit {
|
||||||
background-color: #45a049;
|
background: linear-gradient(145deg, #dc3545, #c82333);
|
||||||
transform: translateY(-2px);
|
box-shadow: 0 2px 8px rgba(220, 53, 69, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-button:hover {
|
.modal-button:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-result {
|
.view-result {
|
||||||
@ -1780,32 +1863,72 @@ export default {
|
|||||||
box-shadow: 0 6px 15px rgba(76, 175, 80, 0.3);
|
box-shadow: 0 6px 15px rgba(76, 175, 80, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.taunt-message {
|
.exit {
|
||||||
margin-top: 15px;
|
background-color: #dc3545;
|
||||||
padding: 10px;
|
|
||||||
background: linear-gradient(145deg, #e53935, #d32f2f);
|
|
||||||
color: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-style: italic;
|
|
||||||
animation: tauntPulse 2s infinite;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
|
||||||
box-shadow: 0 4px 15px rgba(229, 57, 53, 0.4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes tauntPulse {
|
.start-screen {
|
||||||
from {
|
text-align: center;
|
||||||
transform: scale(1);
|
z-index: 10;
|
||||||
box-shadow: 0 4px 15px rgba(229, 57, 53, 0.4);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
}
|
/* padding: 25px 40px; */
|
||||||
50% {
|
/* border-radius: 12px; */
|
||||||
transform: scale(1.05);
|
/* box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); */
|
||||||
box-shadow: 0 6px 20px rgba(229, 57, 53, 0.6);
|
backdrop-filter: blur(4px);
|
||||||
}
|
min-width: 240px;
|
||||||
100% {
|
margin: 20px auto;
|
||||||
transform: scale(1);
|
width: 80%;
|
||||||
box-shadow: 0 4px 15px rgba(229, 57, 53, 0.4);
|
max-width: 300px;
|
||||||
}
|
position: relative;
|
||||||
|
/* border: 1px solid rgba(0, 0, 0, 0.1); */
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button {
|
||||||
|
padding: 12px 36px;
|
||||||
|
font-size: 18px;
|
||||||
|
background: linear-gradient(145deg, #43a047, #2e7d32);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 4px 15px rgba(67, 160, 71, 0.3);
|
||||||
|
width: 100%;
|
||||||
|
font-weight: 500;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(67, 160, 71, 0.4);
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||||
|
transition: 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button:hover::after {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.turn-info {
|
||||||
|
color: #2c3e50;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 8px;
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
@import './base.css';
|
@import './base.css';
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
max-width: 1280px;
|
/* max-width: 1280px; */
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 2rem;
|
/* padding: 2rem; */
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +30,6 @@ a,
|
|||||||
#app {
|
#app {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
padding: 0 2rem;
|
/* padding: 0 2rem; */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,53 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="statistics-panel">
|
<div class="statistics-panel">
|
||||||
<h3>战绩统计</h3>
|
|
||||||
<div class="stats-grid">
|
<div class="stats-grid">
|
||||||
<div class="stat-item">
|
<div class="stats-row">
|
||||||
<div class="stat-label">总对局</div>
|
<div class="stat-item">
|
||||||
<div class="stat-value">{{ totalGames }}</div>
|
<div class="stat-label">总对局</div>
|
||||||
<div class="stat-unit">局</div>
|
<div class="stat-value">{{ totalGames }}</div>
|
||||||
</div>
|
<div class="stat-unit">局</div>
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-label">胜率</div>
|
|
||||||
<div
|
|
||||||
class="stat-value"
|
|
||||||
:class="{ 'win-rate': true, positive: winRate > 50, negative: winRate < 50 }"
|
|
||||||
>
|
|
||||||
{{ formatPercent(winRate) }}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-unit">%</div>
|
<div class="stat-item">
|
||||||
</div>
|
<div class="stat-label">胜率</div>
|
||||||
<div class="stat-item">
|
<div
|
||||||
<div class="stat-label">玩家胜场</div>
|
class="stat-value"
|
||||||
<div class="stat-value">{{ playerWins }}</div>
|
:class="{ 'win-rate': true, positive: winRate > 50, negative: winRate < 50 }"
|
||||||
<div class="stat-unit">胜</div>
|
>
|
||||||
</div>
|
{{ formatPercent(winRate) }}
|
||||||
<div class="stat-item">
|
</div>
|
||||||
<div class="stat-label">电脑胜场</div>
|
<div class="stat-unit">%</div>
|
||||||
<div class="stat-value">{{ computerWins }}</div>
|
|
||||||
<div class="stat-unit">胜</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-label">最大连胜</div>
|
|
||||||
<div class="stat-value">{{ maxStreak }}</div>
|
|
||||||
<div class="stat-unit">连胜</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-label">平均步数</div>
|
|
||||||
<div class="stat-value">{{ avgMoves }}</div>
|
|
||||||
<div class="stat-unit">步</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="stats-chart">
|
|
||||||
<div class="chart-label">胜率分布</div>
|
|
||||||
<div class="win-bars">
|
|
||||||
<div class="win-bar player" :style="{ width: playerBarWidth }">
|
|
||||||
<span class="bar-label">玩家</span>
|
|
||||||
<span class="bar-value">{{ formatPercent(playerWinRate) }}%</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="win-bar computer" :style="{ width: computerBarWidth }">
|
<div class="stat-item">
|
||||||
<span class="bar-label">电脑</span>
|
<div class="stat-label">玩家胜场</div>
|
||||||
<span class="bar-value">{{ formatPercent(computerWinRate) }}%</span>
|
<div class="stat-value">{{ playerWins }}</div>
|
||||||
|
<div class="stat-unit">胜</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-label">电脑胜场</div>
|
||||||
|
<div class="stat-value">{{ computerWins }}</div>
|
||||||
|
<div class="stat-unit">胜</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-row">
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-label">最大连胜</div>
|
||||||
|
<div class="stat-value">{{ maxStreak }}</div>
|
||||||
|
<div class="stat-unit">连胜</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-label">最大连败</div>
|
||||||
|
<div class="stat-value">{{ maxLoseStreak }}</div>
|
||||||
|
<div class="stat-unit">连败</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-label">平均步数</div>
|
||||||
|
<div class="stat-value">{{ avgMoves }}</div>
|
||||||
|
<div class="stat-unit">步</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -76,6 +72,9 @@ export default {
|
|||||||
maxStreak() {
|
maxStreak() {
|
||||||
return this.stats?.winningStreaks?.max || 0
|
return this.stats?.winningStreaks?.max || 0
|
||||||
},
|
},
|
||||||
|
maxLoseStreak() {
|
||||||
|
return this.stats?.winningStreaks?.maxLose || 0
|
||||||
|
},
|
||||||
avgMoves() {
|
avgMoves() {
|
||||||
return this.stats?.avgMoves || 0
|
return this.stats?.avgMoves || 0
|
||||||
},
|
},
|
||||||
@ -83,20 +82,6 @@ export default {
|
|||||||
if (!this.totalGames) return 0
|
if (!this.totalGames) return 0
|
||||||
return (this.playerWins / this.totalGames) * 100
|
return (this.playerWins / this.totalGames) * 100
|
||||||
},
|
},
|
||||||
playerWinRate() {
|
|
||||||
if (!this.totalGames) return 0
|
|
||||||
return (this.playerWins / this.totalGames) * 100
|
|
||||||
},
|
|
||||||
computerWinRate() {
|
|
||||||
if (!this.totalGames) return 0
|
|
||||||
return (this.computerWins / this.totalGames) * 100
|
|
||||||
},
|
|
||||||
playerBarWidth() {
|
|
||||||
return `${Math.max(this.playerWinRate, 20)}%`
|
|
||||||
},
|
|
||||||
computerBarWidth() {
|
|
||||||
return `${Math.max(this.computerWinRate, 20)}%`
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatPercent(value) {
|
formatPercent(value) {
|
||||||
@ -108,49 +93,69 @@ export default {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.statistics-panel {
|
.statistics-panel {
|
||||||
margin: 20px 0;
|
margin: 15px 0;
|
||||||
padding: 20px;
|
padding: 15px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
|
||||||
}
|
|
||||||
|
|
||||||
.statistics-panel h3 {
|
|
||||||
color: #2c3e50;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
font-size: 18px;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-grid {
|
.stats-grid {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
flex-direction: column;
|
||||||
gap: 15px;
|
gap: 12px;
|
||||||
margin-bottom: 20px;
|
}
|
||||||
|
|
||||||
|
.stats-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-item {
|
.stat-item {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 120px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 15px 10px;
|
padding: 12px 8px;
|
||||||
background: #f8f9fa;
|
background: #f8f9fa;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
transition: transform 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-item:hover {
|
.stat-item:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
border-color: rgba(76, 175, 80, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 3px;
|
||||||
|
background: linear-gradient(90deg, #4caf50, #45a049);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item:hover::before {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-label {
|
.stat-label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 6px;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-value {
|
.stat-value {
|
||||||
@ -159,77 +164,45 @@ export default {
|
|||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
transition: color 0.3s ease;
|
transition: color 0.3s ease;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-unit {
|
.stat-unit {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
|
font-weight: 500;
|
||||||
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.win-rate {
|
.win-rate {
|
||||||
font-size: 28px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.win-rate.positive {
|
.win-rate.positive {
|
||||||
color: #4caf50;
|
color: #2e7d32;
|
||||||
|
text-shadow: 0 0 10px rgba(76, 175, 80, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.win-rate.negative {
|
.win-rate.negative {
|
||||||
color: #f44336;
|
color: #c62828;
|
||||||
|
text-shadow: 0 0 10px rgba(244, 67, 54, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-chart {
|
@media (max-width: 768px) {
|
||||||
margin-top: 20px;
|
.stats-row {
|
||||||
background: #f8f9fa;
|
flex-wrap: wrap;
|
||||||
padding: 20px;
|
}
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
.stat-item {
|
||||||
|
width: calc(50% - 6px);
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-label {
|
@media (max-width: 480px) {
|
||||||
font-size: 14px;
|
.stat-item {
|
||||||
color: #6c757d;
|
width: 100%;
|
||||||
margin-bottom: 15px;
|
}
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.win-bars {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.win-bar {
|
|
||||||
height: 32px;
|
|
||||||
margin: 0;
|
|
||||||
color: white;
|
|
||||||
border-radius: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 15px;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
min-width: 120px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-label {
|
|
||||||
font-weight: 500;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bar-value {
|
|
||||||
font-weight: 600;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.win-bar.player {
|
|
||||||
background: linear-gradient(90deg, #4caf50 0%, #45a049 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.win-bar.computer {
|
|
||||||
background: linear-gradient(90deg, #f44336 0%, #e53935 100%);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user