继续落子修复;新增战绩;优化UI
This commit is contained in:
parent
af7afc040d
commit
c2a44ba7c7
379
src/App.vue
379
src/App.vue
@ -76,25 +76,57 @@
|
||||
<!-- 胜利弹窗 -->
|
||||
<div v-if="winner" class="victory-modal">
|
||||
<div class="modal-content">
|
||||
<h2>🎉 游戏结束 🎉</h2>
|
||||
<p v-if="winner === 'timeout'">
|
||||
{{ getPlayerName(currentPlayer === 1 ? 2 : 1) }}胜利!<br />对手超时三次被判负
|
||||
</p>
|
||||
<p v-else>
|
||||
{{ getPlayerName(winner) }}胜利!<br />
|
||||
第 {{ moveCount }} 手获胜
|
||||
</p>
|
||||
<GameStatistics v-if="gameMode === 'pve'" :stats="statistics" />
|
||||
<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">退出游戏</button>
|
||||
<div class="victory-header">
|
||||
<div class="victory-emoji">
|
||||
<template v-if="gameMode === 'pve'">
|
||||
<template v-if="winner === (isPlayerBlack ? 1 : 2)">
|
||||
<!-- 玩家胜利 -->
|
||||
🎉
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- 玩家失败 -->
|
||||
😢
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- PVP模式 -->
|
||||
🎉
|
||||
</template>
|
||||
</div>
|
||||
<h2>游戏结束</h2>
|
||||
</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>
|
||||
@ -197,6 +229,7 @@ export default {
|
||||
winningStreaks: {
|
||||
current: 0,
|
||||
max: 0,
|
||||
maxLose: 0,
|
||||
player: 0,
|
||||
computer: 0,
|
||||
},
|
||||
@ -242,7 +275,15 @@ export default {
|
||||
},
|
||||
|
||||
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()
|
||||
|
||||
@ -313,7 +354,6 @@ export default {
|
||||
|
||||
handleTimeout(player) {
|
||||
this.stopTimer()
|
||||
console.log('超时玩家:', player === 1 ? '黑方' : '白方')
|
||||
|
||||
// 记录当前状态
|
||||
const isPlayerTurn =
|
||||
@ -457,7 +497,6 @@ export default {
|
||||
if (!this.isValidPosition(newRow, newCol)) break
|
||||
const nextCell = this.board[newRow][newCol]
|
||||
if (nextCell.player !== playerType) {
|
||||
console.log('遇到非己方棋子:', newRow, newCol)
|
||||
break
|
||||
}
|
||||
pieces.push([newRow, newCol])
|
||||
@ -471,20 +510,16 @@ export default {
|
||||
if (!this.isValidPosition(newRow, newCol)) break
|
||||
const nextCell = this.board[newRow][newCol]
|
||||
if (nextCell.player !== playerType) {
|
||||
console.log('遇到非己方棋子:', newRow, newCol)
|
||||
break
|
||||
}
|
||||
pieces.unshift([newRow, newCol])
|
||||
count++
|
||||
}
|
||||
|
||||
console.log('找到的棋子:', pieces)
|
||||
|
||||
// 严格检查是否形成五子连珠
|
||||
const isWin = count === 5 && pieces.length === 5
|
||||
|
||||
if (isWin) {
|
||||
console.log('确认五子连珠!玩家:', playerType, '位置:', pieces)
|
||||
this.playWinningAnimation()
|
||||
return {
|
||||
isWin: true,
|
||||
@ -520,14 +555,12 @@ export default {
|
||||
|
||||
// 检查相邻棋子的距离是否严格为1,且方向一致
|
||||
if (curr[0] - prev[0] !== dx || curr[1] - prev[1] !== dy) {
|
||||
console.log('棋子不连续或方向不一致:', curr, prev)
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否属于同一玩家
|
||||
const currPiece = this.board[curr[0]][curr[1]]
|
||||
if (this.isPlayerPiece(currPiece) !== isHuman) {
|
||||
console.log('棋子属于不同玩家:', curr)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -536,13 +569,11 @@ export default {
|
||||
|
||||
// 验证获胜棋子
|
||||
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 this.isValidPosition(row, col) && this.board[row][col].player === player
|
||||
})
|
||||
@ -1200,6 +1231,10 @@ export default {
|
||||
if (this.currentPlayer === (this.isPlayerBlack ? 1 : 2)) {
|
||||
stats.computerWins++
|
||||
stats.winningStreaks.current = Math.min(0, stats.winningStreaks.current) - 1
|
||||
stats.winningStreaks.maxLose = Math.max(
|
||||
stats.winningStreaks.maxLose,
|
||||
Math.abs(stats.winningStreaks.current),
|
||||
)
|
||||
} else {
|
||||
stats.playerWins++
|
||||
stats.winningStreaks.current = Math.max(0, stats.winningStreaks.current) + 1
|
||||
@ -1214,8 +1249,8 @@ export default {
|
||||
} else {
|
||||
stats.computerWins++
|
||||
stats.winningStreaks.current = Math.min(0, stats.winningStreaks.current) - 1
|
||||
stats.winningStreaks.computer = Math.max(
|
||||
stats.winningStreaks.computer,
|
||||
stats.winningStreaks.maxLose = Math.max(
|
||||
stats.winningStreaks.maxLose,
|
||||
Math.abs(stats.winningStreaks.current),
|
||||
)
|
||||
}
|
||||
@ -1294,10 +1329,27 @@ export default {
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
padding: 40px;
|
||||
padding: 30px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
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 {
|
||||
@ -1655,105 +1707,146 @@ export default {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.victory-modal .modal-content {
|
||||
.modal-content {
|
||||
background: white;
|
||||
padding: 40px;
|
||||
padding: 30px;
|
||||
border-radius: 16px;
|
||||
text-align: center;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
animation: modalFadeIn 0.3s ease-out;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.victory-modal h2 {
|
||||
font-size: 28px;
|
||||
color: #2c3e50;
|
||||
.victory-header {
|
||||
text-align: center;
|
||||
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 {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
margin-top: 30px;
|
||||
margin-top: 20px;
|
||||
padding: 10px 0;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.modal-button {
|
||||
padding: 12px 24px;
|
||||
font-size: 16px;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal-button.primary {
|
||||
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;
|
||||
min-width: 100px;
|
||||
font-weight: 500;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.modal-button.secondary {
|
||||
background-color: #6c757d;
|
||||
}
|
||||
|
||||
.modal-button.secondary:hover {
|
||||
background-color: #5a6268;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 2px 8px rgba(108, 117, 125, 0.2);
|
||||
background: linear-gradient(145deg, #6c757d, #5a6268);
|
||||
}
|
||||
|
||||
.modal-button.primary {
|
||||
background-color: #4caf50;
|
||||
box-shadow: 0 2px 8px rgba(76, 175, 80, 0.2);
|
||||
background: linear-gradient(145deg, #4caf50, #45a049);
|
||||
}
|
||||
|
||||
.modal-button.primary:hover {
|
||||
background-color: #45a049;
|
||||
transform: translateY(-2px);
|
||||
.modal-button.exit {
|
||||
background: linear-gradient(145deg, #dc3545, #c82333);
|
||||
box-shadow: 0 2px 8px rgba(220, 53, 69, 0.2);
|
||||
}
|
||||
|
||||
.modal-button:hover {
|
||||
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 {
|
||||
@ -1780,32 +1873,72 @@ export default {
|
||||
box-shadow: 0 6px 15px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.taunt-message {
|
||||
margin-top: 15px;
|
||||
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);
|
||||
.exit {
|
||||
background-color: #dc3545;
|
||||
}
|
||||
|
||||
@keyframes tauntPulse {
|
||||
from {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 4px 15px rgba(229, 57, 53, 0.4);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 6px 20px rgba(229, 57, 53, 0.6);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 4px 15px rgba(229, 57, 53, 0.4);
|
||||
}
|
||||
.start-screen {
|
||||
text-align: center;
|
||||
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;
|
||||
margin: 20px auto;
|
||||
width: 80%;
|
||||
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>
|
||||
|
@ -1,53 +1,49 @@
|
||||
<template>
|
||||
<div class="statistics-panel">
|
||||
<h3>战绩统计</h3>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">总对局</div>
|
||||
<div class="stat-value">{{ totalGames }}</div>
|
||||
<div class="stat-unit">局</div>
|
||||
</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 class="stats-row">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">总对局</div>
|
||||
<div class="stat-value">{{ totalGames }}</div>
|
||||
<div class="stat-unit">局</div>
|
||||
</div>
|
||||
<div class="stat-unit">%</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">玩家胜场</div>
|
||||
<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 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 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 class="stat-unit">%</div>
|
||||
</div>
|
||||
<div class="win-bar computer" :style="{ width: computerBarWidth }">
|
||||
<span class="bar-label">电脑</span>
|
||||
<span class="bar-value">{{ formatPercent(computerWinRate) }}%</span>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">玩家胜场</div>
|
||||
<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>
|
||||
@ -76,6 +72,9 @@ export default {
|
||||
maxStreak() {
|
||||
return this.stats?.winningStreaks?.max || 0
|
||||
},
|
||||
maxLoseStreak() {
|
||||
return this.stats?.winningStreaks?.maxLose || 0
|
||||
},
|
||||
avgMoves() {
|
||||
return this.stats?.avgMoves || 0
|
||||
},
|
||||
@ -83,20 +82,6 @@ export default {
|
||||
if (!this.totalGames) return 0
|
||||
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: {
|
||||
formatPercent(value) {
|
||||
@ -108,49 +93,69 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.statistics-panel {
|
||||
margin: 20px 0;
|
||||
padding: 20px;
|
||||
margin: 15px 0;
|
||||
padding: 15px;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.statistics-panel h3 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 20px;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stats-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
text-align: center;
|
||||
padding: 15px 10px;
|
||||
padding: 12px 8px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.stat-item:hover {
|
||||
transform: translateY(-2px);
|
||||
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 {
|
||||
font-size: 14px;
|
||||
color: #6c757d;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
@ -159,77 +164,45 @@ export default {
|
||||
color: #2c3e50;
|
||||
transition: color 0.3s ease;
|
||||
margin-bottom: 2px;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.stat-unit {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
font-weight: 500;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.win-rate {
|
||||
font-size: 28px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.win-rate.positive {
|
||||
color: #4caf50;
|
||||
color: #2e7d32;
|
||||
text-shadow: 0 0 10px rgba(76, 175, 80, 0.3);
|
||||
}
|
||||
|
||||
.win-rate.negative {
|
||||
color: #f44336;
|
||||
color: #c62828;
|
||||
text-shadow: 0 0 10px rgba(244, 67, 54, 0.3);
|
||||
}
|
||||
|
||||
.stats-chart {
|
||||
margin-top: 20px;
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
@media (max-width: 768px) {
|
||||
.stats-row {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
width: calc(50% - 6px);
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-label {
|
||||
font-size: 14px;
|
||||
color: #6c757d;
|
||||
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%);
|
||||
@media (max-width: 480px) {
|
||||
.stat-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user