vue3项目 / 三子棋

参考: https://blog.csdn.net/iron_nini/article/details/130369453

App.vue

javascript 复制代码
<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

javascript 复制代码
<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>

效果预览



相关推荐
LaughingZhu6 小时前
Product Hunt 每日热榜 | 2026-05-21
前端·人工智能·经验分享·chatgpt·html
怕浪猫6 小时前
Electron 开发实战(一):从零入门核心基础与环境搭建
前端·electron·ai编程
曲幽6 小时前
我用了FastApiAdmin后,连夜把踩过的坑都整理出来了
redis·python·postgresql·vue3·fastapi·web·sqlalchemy·admin·fastapiadmin
小鹏linux6 小时前
Ubuntu 22.04 部署开源免费具有精美现代web页面的Casdoor账号管理系统
linux·前端·ubuntu·开源·堡垒机
前端若水7 小时前
会话管理:创建、切换、删除对话历史
前端·人工智能·python·react.js
Bigger8 小时前
mini-cc:一个轻量级 AI 编程助手的诞生
前端·ai编程·claude
涵涵(互关)8 小时前
Naive-ui树型选择器只显示根节点
前端·ui·vue
BY组态8 小时前
Ricon组态系统最佳实践:从零开始构建物联网监控平台
前端·物联网·iot·web组态·组态
BY组态8 小时前
Ricon组态系统vs传统组态软件:为什么选择新一代Web组态平台
前端·物联网·iot·web组态·组态
SoaringHeart8 小时前
Flutter进阶:OverlayEntry 插入图层管理器 NOverlayZIndexManager
前端·flutter