参考:
https://blog.csdn.net/iron_nini/article/details/130369453
App.vue
<template>
<div class="box">
<div style="margin-bottom: 10px; font-size: 32px">{{ status }}</div>
<div class="board-row">
<squareComponent
v-for="(item, index) in squareValues"
:key="index"
:text="item"
@on-click="handleClick(index)"/>
</div>
<button id="again"
@click="handleAgain"
>Again</button>
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue';
import squareComponent from './squareComponent';
const squareValues = ref(Array(9).fill(''));
const xIsNext = ref(true);
const status = ref('');
const calculateWinner = (squares) => {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for(let i = 0; i < lines.length; i++) {
const [a, b, c] = lines[i];
if(squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
watchEffect(() => {
if(calculateWinner(squareValues.value)){
status.value = 'Winner: ' + calculateWinner(squareValues.value) + ' ! ';
}
else if(!squareValues.value.filter(item => item === '').length){
status.value = 'Draw !';
}
else{
status.value = 'Next player is ' + (xIsNext.value ? 'X' : 'O');
}
});
const handleClick = (index) => {
if(squareValues[index] || calculateWinner(squareValues.value)) {
return;
}
if(xIsNext.value) {
squareValues.value[index] = 'X';
}
else {
squareValues.value[index] = 'O';
}
xIsNext.value = !xIsNext.value;
}
const handleAgain = () => {
squareValues.value = Array(9).fill('');
xIsNext.value = true;
}
</script>
<style scoped>
.box {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: lightgreen;
}
.board-row {
display: grid;
grid-template-columns: repeat(3, minmax(0,1fr));
}
#again {
font-size: 32px;
font-family:'Times New Roman', Times, serif;
margin-top: 20px;
}
</style>
squareComponent.vue
<template>
<button class="square" @click="handleClick">{{ text }}</button>
</template>
<script>
export default{
name: 'squareComponent',
props:{
text:{
type: String,
default: ''
}
},
emits: ['on-click'],
setup(props, context){
const handleClick = () =>{
context.emit('on-click', props.text);
};
return {
handleClick
};
},
};
</script>
<style scoped lang="less">
.square{
box-sizing: border-box;
width: 100px;
height: 100px;
margin: 10px;
border-radius: 50%;
background: pink;
color: black;
font-weight: 700;
font-family: 'Times New Roman', Times, serif;
font-size: 32px;
}
</style>
效果预览