From f1101db237983bd29da4972828d2932a64692816 Mon Sep 17 00:00:00 2001 From: flykhan Date: Wed, 22 Feb 2023 10:26:52 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=84=E6=A0=BC=E5=8C=96=20js=20=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/assets/scripts/AcGameObject.js | 123 ++++--- web/src/assets/scripts/Cell.js | 18 +- web/src/assets/scripts/GameMap.js | 453 +++++++++++++------------ web/src/assets/scripts/Snake.js | 414 +++++++++++----------- web/src/assets/scripts/Wall.js | 53 ++- web/src/router/index.js | 118 ++++--- web/src/store/index.js | 21 +- web/src/store/user.js | 171 +++++----- 8 files changed, 702 insertions(+), 669 deletions(-) diff --git a/web/src/assets/scripts/AcGameObject.js b/web/src/assets/scripts/AcGameObject.js index 31fa5b6..a9db9c4 100644 --- a/web/src/assets/scripts/AcGameObject.js +++ b/web/src/assets/scripts/AcGameObject.js @@ -4,80 +4,71 @@ const AC_GAME_OBJECTS = []; // 导出类 export class AcGameObject { - // 构造函数 - constructor(){ - // push(this) 是将当前对象存下来的意思 - // 每创建一个,就 push 一个,先创建先 push,后创建后 push - // 先创建的先执行 update ,后创建的会把先创建的给覆盖掉 - AC_GAME_OBJECTS.push(this); - // 帧与帧执行的时间间隔,单位:秒 - this.timedelta=0; - // 是否执行过 start 函数 - this.has_called_start = false; - } - - // start 函数只执行一次 - start(){ - - } - - // 除第一帧之外,每一帧执行一遍 - update(){ - - } - - // 删除之前执行 - on_destroy(){ - - } - - // 删除 - destroy(){ - // 删除之前调用 on_destroy 函数 - this.on_destroy(); - - // 在 js 里,使用 of 遍历的是数组里的值;使用 in 遍历的是数组的下标。 - for(let i in AC_GAME_OBJECTS){ - const obj = AC_GAME_OBJECTS[i]; - // 如果 obj 等于当前对象,则删除该对象 - if(obj === this){ - // 使用 splice 删除数组里的对象 - AC_GAME_OBJECTS.splice(i); - break; - } - } - + // 构造函数 + constructor() { + // push(this) 是将当前对象存下来的意思 + // 每创建一个,就 push 一个,先创建先 push,后创建后 push + // 先创建的先执行 update ,后创建的会把先创建的给覆盖掉 + AC_GAME_OBJECTS.push(this); + // 帧与帧执行的时间间隔,单位:秒 + this.timedelta = 0; + // 是否执行过 start 函数 + this.has_called_start = false; + } + + // start 函数只执行一次 + start() {} + + // 除第一帧之外,每一帧执行一遍 + update() {} + + // 删除之前执行 + on_destroy() {} + + // 删除 + destroy() { + // 删除之前调用 on_destroy 函数 + this.on_destroy(); + + // 在 js 里,使用 of 遍历的是数组里的值;使用 in 遍历的是数组的下标。 + for (let i in AC_GAME_OBJECTS) { + const obj = AC_GAME_OBJECTS[i]; + // 如果 obj 等于当前对象,则删除该对象 + if (obj === this) { + // 使用 splice 删除数组里的对象 + AC_GAME_OBJECTS.splice(i); + break; + } } + } } - // 上一帧执行的时刻 let last_timestamp; // step 函数需要传入当前帧执行的时刻 timestamp const step = (timestamp) => { - // 遍历所有的物品 - // 在 js 里,使用 of 遍历的是数组里的值;使用 in 遍历的是数组的下标。 - for(let obj of AC_GAME_OBJECTS){ - // 如果当前物品没有执行 start 函数,则该物品执行一次 start 函数 - if(!obj.has_called_start){ - // 将该物品的 has_called_start 赋值为 true,表示其已经执行过了 - obj.has_called_start = true; - obj.start(); - } - // 如果执行过 start ,则接下来应该执行 update 函数 - else{ - // 当前帧与上一帧的时间间隔:当前帧执行时刻减去上一帧执行时刻 - obj.timedelta = timestamp - last_timestamp; - obj.update(); - } + // 遍历所有的物品 + // 在 js 里,使用 of 遍历的是数组里的值;使用 in 遍历的是数组的下标。 + for (let obj of AC_GAME_OBJECTS) { + // 如果当前物品没有执行 start 函数,则该物品执行一次 start 函数 + if (!obj.has_called_start) { + // 将该物品的 has_called_start 赋值为 true,表示其已经执行过了 + obj.has_called_start = true; + obj.start(); } + // 如果执行过 start ,则接下来应该执行 update 函数 + else { + // 当前帧与上一帧的时间间隔:当前帧执行时刻减去上一帧执行时刻 + obj.timedelta = timestamp - last_timestamp; + obj.update(); + } + } - // 更新 last_timestamp ,作为下一次更新的“上一帧执行的时刻” - last_timestamp = timestamp; - // 递归调用 - requestAnimationFrame(step) - -} + // 更新 last_timestamp ,作为下一次更新的“上一帧执行的时刻” + last_timestamp = timestamp; + // 递归调用 + requestAnimationFrame(step); +}; // 定义需要的刷新次数,传入的函数step会在下一帧浏览器渲染之前执行一遍。 -requestAnimationFrame(step) \ No newline at end of file +requestAnimationFrame(step); diff --git a/web/src/assets/scripts/Cell.js b/web/src/assets/scripts/Cell.js index 16d5984..bff97a4 100644 --- a/web/src/assets/scripts/Cell.js +++ b/web/src/assets/scripts/Cell.js @@ -1,11 +1,11 @@ // 定义一个格子 export class Cell { - // 参数是格子的行数和列数 - constructor(r,c){ - this.r = r; - this.c = c; - // 坐标转换,用于确定蛇的每节身体所在格子中心点的坐标 - this.x = c + 0.5; - this.y = r + 0.5; - } -} \ No newline at end of file + // 参数是格子的行数和列数 + constructor(r, c) { + this.r = r; + this.c = c; + // 坐标转换,用于确定蛇的每节身体所在格子中心点的坐标 + this.x = c + 0.5; + this.y = r + 0.5; + } +} diff --git a/web/src/assets/scripts/GameMap.js b/web/src/assets/scripts/GameMap.js index 0652aa1..5392d7a 100644 --- a/web/src/assets/scripts/GameMap.js +++ b/web/src/assets/scripts/GameMap.js @@ -5,255 +5,260 @@ import { Snake } from "./Snake"; import { Wall } from "./Wall"; // 导出定义的 GameMap 游戏地图类 -export class GameMap extends AcGameObject{ - // 构造函数参数: ctx 画布; parent 画布的父元素,用来动态修改画布的长宽 - constructor(ctx,parent){ - // super() 用于先执行基类的构造函数 - super(); +export class GameMap extends AcGameObject { + // 构造函数参数: ctx 画布; parent 画布的父元素,用来动态修改画布的长宽 + constructor(ctx, parent) { + // super() 用于先执行基类的构造函数 + super(); - // 存下 ctx 和 parent - this.ctx = ctx; - this.parent = parent; + // 存下 ctx 和 parent + this.ctx = ctx; + this.parent = parent; - // 存下每个格子的绝对距离 - this.L = 0; + // 存下每个格子的绝对距离 + this.L = 0; - // 定义棋盘格的行数和列数 - // 行数和列数不同时设置为偶数或者不同时设置为奇数,可以避免AB两蛇同时进入同一个格子,避免因此对优势者不公平 - this.rows = 13; - this.cols = 14; + // 定义棋盘格的行数和列数 + // 行数和列数不同时设置为偶数或者不同时设置为奇数,可以避免AB两蛇同时进入同一个格子,避免因此对优势者不公平 + this.rows = 13; + this.cols = 14; - // 绘制棋盘内部区域的障碍物(墙)的数量 - this.inner_walls_count = 20; + // 绘制棋盘内部区域的障碍物(墙)的数量 + this.inner_walls_count = 20; - // 存储所有的墙 - // 上面的 super() 会先将 AcGameObject 先绘制, walls 的绘制在后面执行,因此墙最后会覆盖原棋盘格进行绘制 - this.walls = []; + // 存储所有的墙 + // 上面的 super() 会先将 AcGameObject 先绘制, walls 的绘制在后面执行,因此墙最后会覆盖原棋盘格进行绘制 + this.walls = []; - // 创建蛇对象数组 - this.snakes = [ - // 注意这里的对象生成方式和传参方式 - new Snake({id: 0, color: "#4876ec", r: this.rows-2, c: 1}, this), - new Snake({id: 1, color: "#f94848", r: 1, c: this.cols-2}, this), - ]; - } + // 创建蛇对象数组 + this.snakes = [ + // 注意这里的对象生成方式和传参方式 + new Snake({ id: 0, color: "#4876ec", r: this.rows - 2, c: 1 }, this), + new Snake({ id: 1, color: "#f94848", r: 1, c: this.cols - 2 }, this), + ]; + } - // 判断函数:判断角色路径是否联通。传入参数:g数组,起点和终点的横纵坐标 - check_connectivity(g, sx, sy, tx, ty){ - // 当起点坐标和中点坐标一致时,判断联通,直接返回 - if(sx == tx && sy == ty) return true; - g[sx][sy] = true; + // 判断函数:判断角色路径是否联通。传入参数:g数组,起点和终点的横纵坐标 + check_connectivity(g, sx, sy, tx, ty) { + // 当起点坐标和中点坐标一致时,判断联通,直接返回 + if (sx == tx && sy == ty) return true; + g[sx][sy] = true; - // 定义四方向偏移量 - let dx = [-1, 0, 1, 0], dy = [0, 1, 0 ,-1]; - // 枚举上下左右四个方向,求当前点下一个相邻点的坐标 - for(let i = 0; i < 4; i++){ - let x = sx + dx[i], y = sy + dy[i]; - // 判断是否撞墙,如果没有撞墙,且可以搜到终点的话,返回 true ,否则返回 false - if(!g[x][y] == true && this.check_connectivity(g, x, y, tx, ty)) - return true; - } - // 搜不到终点,返回 false - return false; - } - - // 创建墙函数 - create_wall(){ - // 创建一个墙格进行测试 - // new Wall(0,0,this); - - // 开一个布尔数组,有墙为 true - // 一开始先将所有墙初始化为 false - const g = []; - for(let r = 0; r < this.rows; r ++){ - g[r] = []; - for(let c = 0; c < this.cols; c ++){ - g[r][c] = false; - } - } - - // 给左右加上墙 - for(let r = 0; r < this.rows; r ++){ - g[r][0] = g[r][this.cols-1] = true; - } - - // 给上下加上墙 - for(let c = 0; c < this.cols; c ++){ - g[0][c] = g[this.rows-1][c] = true; - } - - // 创建内部随机障碍物 - // 因为每次计算都会生成两个障碍物,因此这里的循环次数 this.inner_walls_count 需要处以 2 - for(let i = 0; i < this.inner_walls_count / 2; i ++){ - // 避免位置重复:重复 1000 次,只要找到了就禁止随机 - for(let j = 0; j < 1000; j ++){ - let r = parseInt(Math.random()*this.rows); - let c = parseInt(Math.random()*this.cols); - - // 主对角线对称 g[r][c] 和 g[c][r] 完成两种联合判断 - // 当此位置已经有障碍物了,则重新计算下一个位置 - // if(g[r][c] || g[c][r]) continue; - // 解决中心对称问题,需要将注释代码修改为下一行 - if(g[r][c] || g[this.rows-1-r][this.cols-1-c]) continue; - - // 将计算求得的随机障碍物的位置置为 true ,以对该位置进行绘制 - // g[r][c] 和 g[c][r] 的坐标在对角线位置会重合,会被绘制为一个障碍物 - // g[r][c] || g[c][r] = true; - // 解决中心对称问题,需要将注释代码修改为下一行 - g[r][c] = g[this.rows-1-r][this.cols-1-c] = true; - - // 1000 次中,规定数量的内部障碍物已经够了之后就 break 掉 - break; - } - } - - // 避免内部障碍物覆盖掉左下角和右上角的角色出发点 - g[this.rows-2][1] = g[1][this.cols-2] = false; - - // 保证两个对角角色的运动区域是联通的 - // 检测联通需要把 g[][] 传过去给 check_connectivity() 函数进行判断,传过去之前需要把当前 g[][] 状态复制一份,避免当前数据被修改掉 - // 深度复制方法:先转换数据为 JSON ,再把 JSON 解析出来 - const copy_g = JSON.parse(JSON.stringify(g)); - // 检测到不连通,则直接在生成对象之前 return false 退出函数 - if(!this.check_connectivity(copy_g, this.rows-2, 1, 1, this.cols-2)) return false; - - // 枚举数组,将 g[r][c] == true 的部分绘制出来 - // 如果上一步连通性检测失败,则退出 this.create_wall() 函数,本步骤不再执行生成新对象的操作 - for(let r = 0; r < this.rows; r ++){ - for(let c = 0; c < this.cols; c ++){ - if(g[r][c]){ - // 将每个新生成的 Wall 对象 push 存入 walls 数组中 - this.walls.push(new Wall(r,c,this)); - } - } - } - - // 绘制成功则 return turn + // 定义四方向偏移量 + let dx = [-1, 0, 1, 0], + dy = [0, 1, 0, -1]; + // 枚举上下左右四个方向,求当前点下一个相邻点的坐标 + for (let i = 0; i < 4; i++) { + let x = sx + dx[i], + y = sy + dy[i]; + // 判断是否撞墙,如果没有撞墙,且可以搜到终点的话,返回 true ,否则返回 false + if (!g[x][y] == true && this.check_connectivity(g, x, y, tx, ty)) return true; } + // 搜不到终点,返回 false + return false; + } - // 添加监听:用于绑定键盘输入,以便获取用户操作控制蛇 - add_listening_events () { - // 聚焦到获取输入的画布页面 - this.ctx.canvas.focus(); + // 创建墙函数 + create_wall() { + // 创建一个墙格进行测试 + // new Wall(0,0,this); - // 先取出两条蛇对象 - const [snake0, snake1] = this.snakes; - - // 获取用户信息:绑定 keydown 事件 - this.ctx.canvas.addEventListener("keydown", e => { - // 定义 snake0 的键盘绑定事件 - if (e.key === 'w') snake0.set_direction(0); - else if (e.key === 'd') snake0.set_direction(1); - else if (e.key === 's') snake0.set_direction(2); - else if (e.key === 'a') snake0.set_direction(3); - - // 定义 snake1 的键盘绑定事件 - else if (e.key === 'ArrowUp') snake1.set_direction(0); - else if (e.key === 'ArrowRight') snake1.set_direction(1); - else if (e.key === 'ArrowDown') snake1.set_direction(2); - else if (e.key === 'ArrowLeft') snake1.set_direction(3); - }); + // 开一个布尔数组,有墙为 true + // 一开始先将所有墙初始化为 false + const g = []; + for (let r = 0; r < this.rows; r++) { + g[r] = []; + for (let c = 0; c < this.cols; c++) { + g[r][c] = false; + } } - start(){ - // 开始时调用一次创建墙的函数 - // 循环 1000 次,如果成功创建则 break ,否则继续循环创建 - for(let i = 0; i < 1000; i ++) - if(this.create_wall()) - break; - - // 开始时启动监听方法 - this.add_listening_events(); + // 给左右加上墙 + for (let r = 0; r < this.rows; r++) { + g[r][0] = g[r][this.cols - 1] = true; } - // 每一帧都更新一下小正方格的边长 - update_size(){ - // 计算当前帧每个格子的宽度, parseInt 取整是为了避免渲染出的格子之间出现小空隙 - this.L = parseInt(Math.min(this.parent.clientWidth / this.cols, this.parent.clientHeight / this.rows)); - // 计算当前画布的宽度 - this.ctx.canvas.width = this.L * this.cols; - // 计算当前画布的高度 - this.ctx.canvas.height = this.L * this.rows; + // 给上下加上墙 + for (let c = 0; c < this.cols; c++) { + g[0][c] = g[this.rows - 1][c] = true; } - // 判断两条蛇是否准保好进入下一回合 - check_ready() { - for (const snake of this.snakes) { - // 判断蛇的状态,当状态不为静止,表示蛇上一步骤没有执行终止,返回 false 表示未准备好进入下一回合 - if (snake.status !== "idle") return false; - // 判断蛇下一步指令的方向,如果为 -1 ,表示蛇目前没有获取到方向指令,返回 false 表示未准备好进入下一回合 - if (snake.direction === -1) return false; + // 创建内部随机障碍物 + // 因为每次计算都会生成两个障碍物,因此这里的循环次数 this.inner_walls_count 需要处以 2 + for (let i = 0; i < this.inner_walls_count / 2; i++) { + // 避免位置重复:重复 1000 次,只要找到了就禁止随机 + for (let j = 0; j < 1000; j++) { + let r = parseInt(Math.random() * this.rows); + let c = parseInt(Math.random() * this.cols); + + // 主对角线对称 g[r][c] 和 g[c][r] 完成两种联合判断 + // 当此位置已经有障碍物了,则重新计算下一个位置 + // if(g[r][c] || g[c][r]) continue; + // 解决中心对称问题,需要将注释代码修改为下一行 + if (g[r][c] || g[this.rows - 1 - r][this.cols - 1 - c]) continue; + + // 将计算求得的随机障碍物的位置置为 true ,以对该位置进行绘制 + // g[r][c] 和 g[c][r] 的坐标在对角线位置会重合,会被绘制为一个障碍物 + // g[r][c] || g[c][r] = true; + // 解决中心对称问题,需要将注释代码修改为下一行 + g[r][c] = g[this.rows - 1 - r][this.cols - 1 - c] = true; + + // 1000 次中,规定数量的内部障碍物已经够了之后就 break 掉 + break; + } + } + + // 避免内部障碍物覆盖掉左下角和右上角的角色出发点 + g[this.rows - 2][1] = g[1][this.cols - 2] = false; + + // 保证两个对角角色的运动区域是联通的 + // 检测联通需要把 g[][] 传过去给 check_connectivity() 函数进行判断,传过去之前需要把当前 g[][] 状态复制一份,避免当前数据被修改掉 + // 深度复制方法:先转换数据为 JSON ,再把 JSON 解析出来 + const copy_g = JSON.parse(JSON.stringify(g)); + // 检测到不连通,则直接在生成对象之前 return false 退出函数 + if (!this.check_connectivity(copy_g, this.rows - 2, 1, 1, this.cols - 2)) + return false; + + // 枚举数组,将 g[r][c] == true 的部分绘制出来 + // 如果上一步连通性检测失败,则退出 this.create_wall() 函数,本步骤不再执行生成新对象的操作 + for (let r = 0; r < this.rows; r++) { + for (let c = 0; c < this.cols; c++) { + if (g[r][c]) { + // 将每个新生成的 Wall 对象 push 存入 walls 数组中 + this.walls.push(new Wall(r, c, this)); } - - // 当上面的条件判断都满足进入下一回合的条件时,返回 true 表示准备好进入下一步 - return true; + } } - // 让两条蛇进入下一回合 - next_step() { - for (const snake of this.snakes) { - snake.next_step(); - } - } - - // 增加碰撞检测:检测目标位置是否合法:没有撞到蛇的身体和墙 - check_valid (cell) { - // 碰墙检测 - for (const wall of this.walls){ - if (wall.r === cell.r && wall.c === cell.c) - return false; - } + // 绘制成功则 return turn + return true; + } - // 蛇身碰撞检测 - for (const snake of this.snakes) { - // 先特判蛇尾碰撞情况 - let k = snake.cells.length; - // 当蛇尾可以前进(蛇尾可以前进说明此回合蛇尾没有增加)的时候,蛇尾不要判断 - if (!snake.check_tail_increasing()) { - k --; - } - // 判断蛇身现有结点是否碰撞 - for (let i = 0; i < k; i++) { - if (snake.cells[i].r === cell.r && snake.cells[i].c === cell.c) - return false - } - } + // 添加监听:用于绑定键盘输入,以便获取用户操作控制蛇 + add_listening_events() { + // 聚焦到获取输入的画布页面 + this.ctx.canvas.focus(); - // 没有撞到障碍物时, return true - return true; + // 先取出两条蛇对象 + const [snake0, snake1] = this.snakes; + + // 获取用户信息:绑定 keydown 事件 + this.ctx.canvas.addEventListener("keydown", (e) => { + // 定义 snake0 的键盘绑定事件 + if (e.key === "w") snake0.set_direction(0); + else if (e.key === "d") snake0.set_direction(1); + else if (e.key === "s") snake0.set_direction(2); + else if (e.key === "a") snake0.set_direction(3); + // 定义 snake1 的键盘绑定事件 + else if (e.key === "ArrowUp") snake1.set_direction(0); + else if (e.key === "ArrowRight") snake1.set_direction(1); + else if (e.key === "ArrowDown") snake1.set_direction(2); + else if (e.key === "ArrowLeft") snake1.set_direction(3); + }); + } + + start() { + // 开始时调用一次创建墙的函数 + // 循环 1000 次,如果成功创建则 break ,否则继续循环创建 + for (let i = 0; i < 1000; i++) if (this.create_wall()) break; + + // 开始时启动监听方法 + this.add_listening_events(); + } + + // 每一帧都更新一下小正方格的边长 + update_size() { + // 计算当前帧每个格子的宽度, parseInt 取整是为了避免渲染出的格子之间出现小空隙 + this.L = parseInt( + Math.min( + this.parent.clientWidth / this.cols, + this.parent.clientHeight / this.rows + ) + ); + // 计算当前画布的宽度 + this.ctx.canvas.width = this.L * this.cols; + // 计算当前画布的高度 + this.ctx.canvas.height = this.L * this.rows; + } + + // 判断两条蛇是否准保好进入下一回合 + check_ready() { + for (const snake of this.snakes) { + // 判断蛇的状态,当状态不为静止,表示蛇上一步骤没有执行终止,返回 false 表示未准备好进入下一回合 + if (snake.status !== "idle") return false; + // 判断蛇下一步指令的方向,如果为 -1 ,表示蛇目前没有获取到方向指令,返回 false 表示未准备好进入下一回合 + if (snake.direction === -1) return false; } - update(){ - this.update_size(); - // 当两条蛇都准备好进入下一回合后 - if (this.check_ready()) { - this.next_step(); - } - // 每次更新都重新执行渲染 - this.render(); + // 当上面的条件判断都满足进入下一回合的条件时,返回 true 表示准备好进入下一步 + return true; + } + + // 让两条蛇进入下一回合 + next_step() { + for (const snake of this.snakes) { + snake.next_step(); + } + } + + // 增加碰撞检测:检测目标位置是否合法:没有撞到蛇的身体和墙 + check_valid(cell) { + // 碰墙检测 + for (const wall of this.walls) { + if (wall.r === cell.r && wall.c === cell.c) return false; } - // 渲染函数,把当前的游戏对象绘制到地图上 - render(){ - // b47226 棕色 aad751 浅绿 a2d048 深绿 - // this.ctx.fillStyle = 'green'; - // this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); - - // 定义偶数格even、奇数格odd的颜色 - const color_even = "#aad751", color_odd = "#a2d048"; - - for(let r = 0; r < this.rows; r++){ - for(let c = 0; c < this.cols; c++){ - // 当列标加行标: r + c 是偶数时,选取偶数颜色,否则选取奇数颜色。 - if((r + c) % 2 == 0){ - this.ctx.fillStyle = color_even; - }else{ - this.ctx.fillStyle = color_odd; - } - // 绘制小方格:起始坐标x,起始坐标y,水平边长,竖直边长 - this.ctx.fillRect(c*this.L, r*this.L, this.L, this.L); - } - } + // 蛇身碰撞检测 + for (const snake of this.snakes) { + // 先特判蛇尾碰撞情况 + let k = snake.cells.length; + // 当蛇尾可以前进(蛇尾可以前进说明此回合蛇尾没有增加)的时候,蛇尾不要判断 + if (!snake.check_tail_increasing()) { + k--; + } + // 判断蛇身现有结点是否碰撞 + for (let i = 0; i < k; i++) { + if (snake.cells[i].r === cell.r && snake.cells[i].c === cell.c) + return false; + } } -} \ No newline at end of file + + // 没有撞到障碍物时, return true + return true; + } + + update() { + this.update_size(); + // 当两条蛇都准备好进入下一回合后 + if (this.check_ready()) { + this.next_step(); + } + // 每次更新都重新执行渲染 + this.render(); + } + + // 渲染函数,把当前的游戏对象绘制到地图上 + render() { + // b47226 棕色 aad751 浅绿 a2d048 深绿 + // this.ctx.fillStyle = 'green'; + // this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); + + // 定义偶数格even、奇数格odd的颜色 + const color_even = "#aad751", + color_odd = "#a2d048"; + + for (let r = 0; r < this.rows; r++) { + for (let c = 0; c < this.cols; c++) { + // 当列标加行标: r + c 是偶数时,选取偶数颜色,否则选取奇数颜色。 + if ((r + c) % 2 == 0) { + this.ctx.fillStyle = color_even; + } else { + this.ctx.fillStyle = color_odd; + } + // 绘制小方格:起始坐标x,起始坐标y,水平边长,竖直边长 + this.ctx.fillRect(c * this.L, r * this.L, this.L, this.L); + } + } + } +} diff --git a/web/src/assets/scripts/Snake.js b/web/src/assets/scripts/Snake.js index 0dbf7f5..ff7db47 100644 --- a/web/src/assets/scripts/Snake.js +++ b/web/src/assets/scripts/Snake.js @@ -4,233 +4,245 @@ import { Cell } from "./Cell"; // 需要继承自绘制基础类 AcGameObject ,用于蛇的绘制刷新 export class Snake extends AcGameObject { - // 参数:蛇的信息 info ,当前游戏地图 gamemap - constructor(info, gamemap) { - super(); + // 参数:蛇的信息 info ,当前游戏地图 gamemap + constructor(info, gamemap) { + super(); - // 每条蛇的 id 用于区分每条蛇, color 用于定义蛇的颜色 - this.id = info.id; - this.color = info.color; - // 调用地图的引用获取一些地图参数,例如:每个格子的边长 - this.gamemap = gamemap; + // 每条蛇的 id 用于区分每条蛇, color 用于定义蛇的颜色 + this.id = info.id; + this.color = info.color; + // 调用地图的引用获取一些地图参数,例如:每个格子的边长 + this.gamemap = gamemap; - // 蛇初始只有一个点(蛇头),初始时只需要定义出蛇头即可。初始坐标为每条蛇的起始位置 - // cells[] 存放蛇的身体, cells[0] 存放蛇头 - this.cells = [new Cell(info.r, info.c)] - this.next_cell = null; // 下一步的目标位置 + // 蛇初始只有一个点(蛇头),初始时只需要定义出蛇头即可。初始坐标为每条蛇的起始位置 + // cells[] 存放蛇的身体, cells[0] 存放蛇头 + this.cells = [new Cell(info.r, info.c)]; + this.next_cell = null; // 下一步的目标位置 - this.speed = 5; // 蛇的速度:每秒走五个格子 + this.speed = 5; // 蛇的速度:每秒走五个格子 - // 定义蛇下一步的指令相关属性 - // -1 表示没有指令, 0、 1、 2、 3 表示上右下左方向 - this.direction = -1; - // idle 表示静止, move 表示移动, die 表示死亡(状态判断的逻辑在蛇群公共部分 GameMap.js 中定义) - this.status = "idle"; + // 定义蛇下一步的指令相关属性 + // -1 表示没有指令, 0、 1、 2、 3 表示上右下左方向 + this.direction = -1; + // idle 表示静止, move 表示移动, die 表示死亡(状态判断的逻辑在蛇群公共部分 GameMap.js 中定义) + this.status = "idle"; - // 4方向行方向偏移量 - this.dr = [-1, 0, 1, 0]; - // 4方向列方向偏移量 - this.dc = [0, 1, 0, -1]; + // 4方向行方向偏移量 + this.dr = [-1, 0, 1, 0]; + // 4方向列方向偏移量 + this.dc = [0, 1, 0, -1]; - // 当前的回合数:前 10 回合,每次蛇身节数都会 +1, 后面每隔 3 回合蛇身节数 +1 - this.step = 0; + // 当前的回合数:前 10 回合,每次蛇身节数都会 +1, 后面每隔 3 回合蛇身节数 +1 + this.step = 0; - // 定义允许的误差: 0.01 ,当误差为 0.01 以内时,就认为两个点已经重合 - this.eps = 1e-2; + // 定义允许的误差: 0.01 ,当误差为 0.01 以内时,就认为两个点已经重合 + this.eps = 1e-2; - // 蛇头的眼睛方向:(默认)左下角蛇初始方向朝上 - this.eye_direction = 0; - // 右上角蛇初始方向朝下 - if (this.id === 1) this.eye_direction = 2; - // 蛇眼睛四方向x偏移量 - this.eye_dx = [ - [-1, 1], - [1, 1], - [1, -1], - [-1, -1], - ]; + // 蛇头的眼睛方向:(默认)左下角蛇初始方向朝上 + this.eye_direction = 0; + // 右上角蛇初始方向朝下 + if (this.id === 1) this.eye_direction = 2; + // 蛇眼睛四方向x偏移量 + this.eye_dx = [ + [-1, 1], + [1, 1], + [1, -1], + [-1, -1], + ]; - // 蛇眼睛四方向y偏移量 - this.eye_dy = [ - [-1, -1], - [-1, 1], - [1, 1], - [1, -1], - ]; + // 蛇眼睛四方向y偏移量 + this.eye_dy = [ + [-1, -1], + [-1, 1], + [1, 1], + [1, -1], + ]; + } + start() {} + + // 定义方向设置接口 + set_direction(d) { + // 可以将当前方向 this.direction 变为 d , d 是通过键盘或者其他方式人为赋予的方向值 + this.direction = d; + } + + // 检测当前回合,蛇尾是否增加 + check_tail_increasing() { + // 前 10 回合每次都增加,后面每 3 回合增加一节蛇尾 + if (this.step <= 10) return true; + if (this.step % 3 === 1) return true; + // 否则 return false + return false; + } + + // 更新当前状态,将蛇的状态变为走下一步 + next_step() { + // 当前的蛇头方向 + const d = this.direction; + // 下一节蛇身体的坐标计算 + this.next_cell = new Cell( + this.cells[0].r + this.dr[d], + this.cells[0].c + this.dc[d] + ); + + // 更新蛇眼睛的方向:就是下一步的蛇头方向 + this.eye_direction = d; + + // 计算完坐标之后,清空方向 + this.direction = -1; + // 将状态从静止变为移动 + this.status = "move"; + + // 增加回合数 + this.step++; + + // 计算新的蛇身体结点 + const k = this.cells.length; + for (let i = k; i > 0; i--) { + // 每个身体结点都要往后移动一位,配合头部新生成的一位结点,共同组成一个新的蛇身 + // 这里需要使用 JSON 方法进行深度复制,以产生新的对象避免数据出错 + this.cells[i] = JSON.parse(JSON.stringify(this.cells[i - 1])); } - start() { - + // 如果下一步操作的目标位置碰撞检测不合法,则蛇直接去世 + if (!this.gamemap.check_valid(this.next_cell)) { + this.status = "die"; } + } - // 定义方向设置接口 - set_direction(d) { - // 可以将当前方向 this.direction 变为 d , d 是通过键盘或者其他方式人为赋予的方向值 - this.direction = d; + update_move() { + // 计算目标方向 dx , dy : 使用目标点的坐标减去当前蛇头的坐标 + const dx = this.next_cell.x - this.cells[0].x; + const dy = this.next_cell.y - this.cells[0].y; + // 蛇头移动到下一步目标点之间,目前已经移动过的距离 + const distance = Math.sqrt(dx * dx + dy * dy); + + // 计算当前身体结点是否走到终点(下一步的目标位置): 已经移动到终点,则停止移动; + if (distance < this.eps) { + // 把目标点存下来作为新的头 + this.cells[0] = this.next_cell; + // 下一步之前,将当前的 this.next_cell 清空 + this.next_cell = null; + + // 将状态改为 idle 表示当前停下的状态 + this.status = "idle"; + + // 如果蛇不边长,则每次移动时,增加头部的同时,把蛇尾砍掉 + if (!this.check_tail_increasing()) { + this.cells.pop(); + } } + // 不重合表示尚未移动到下一步,还可以继续移动 + else { + // 按时间(second)定义移动距离: 每一帧走过的距离 = 速度 * 两帧时间间隔 / 1000(ms) + const move_distance = (this.speed * this.timedelta) / 1000; // 处以 1000 ,将毫秒单位转换成秒单位 + this.cells[0].x += (move_distance * dx) / distance; + this.cells[0].y += (move_distance * dy) / distance; - // 检测当前回合,蛇尾是否增加 - check_tail_increasing() { - // 前 10 回合每次都增加,后面每 3 回合增加一节蛇尾 - if(this.step <= 10) return true; - if(this.step % 3 === 1) return true; - // 否则 return false - return false; - } - - // 更新当前状态,将蛇的状态变为走下一步 - next_step() { - // 当前的蛇头方向 - const d = this.direction; - // 下一节蛇身体的坐标计算 - this.next_cell = new Cell(this.cells[0].r + this.dr[d], this.cells[0].c + this.dc[d]); - - // 更新蛇眼睛的方向:就是下一步的蛇头方向 - this.eye_direction = d; - - // 计算完坐标之后,清空方向 - this.direction = -1; - // 将状态从静止变为移动 - this.status = "move"; - - // 增加回合数 - this.step ++; - - // 计算新的蛇身体结点 + // 更新蛇尾位置 + if (!this.check_tail_increasing()) { const k = this.cells.length; - for(let i = k; i > 0; i --) { - // 每个身体结点都要往后移动一位,配合头部新生成的一位结点,共同组成一个新的蛇身 - // 这里需要使用 JSON 方法进行深度复制,以产生新的对象避免数据出错 - this.cells[i] = JSON.parse(JSON.stringify(this.cells[i - 1])); - } + // 取出当前的蛇尾 + const tail = this.cells[k - 1]; + // 当前蛇尾的下一个目标位置 + const tail_target = this.cells[k - 2]; - // 如果下一步操作的目标位置碰撞检测不合法,则蛇直接去世 - if (!this.gamemap.check_valid(this.next_cell)){ - this.status = "die"; - } + // 把当前蛇尾移动到下一个蛇尾目标位置: 将 tail 移动到 tail_target 位置 + // 求两个位置的横纵坐标差值 + const tail_dx = tail_target.x - tail.x; + const tail_dy = tail_target.y - tail.y; + + // 移动蛇尾 + tail.x += (move_distance * tail_dx) / distance; + tail.y += (move_distance * tail_dy) / distance; + } + } + } + + // update 方法每一帧执行一次,每秒钟执行 60 次 + update() { + // 当蛇处于 move 移动状态时, 使用 update_move 方法更新蛇的移动 + if (this.status === "move") { + // 每一帧调用 this.update_move() + this.update_move(); } + // 每次更新蛇类对象都重新调用一次渲染 + this.render(); + } - update_move(){ - // 计算目标方向 dx , dy : 使用目标点的坐标减去当前蛇头的坐标 - const dx = this.next_cell.x - this.cells[0].x; - const dy = this.next_cell.y - this.cells[0].y; - // 蛇头移动到下一步目标点之间,目前已经移动过的距离 - const distance = Math.sqrt(dx * dx + dy * dy); - - // 计算当前身体结点是否走到终点(下一步的目标位置): 已经移动到终点,则停止移动; - if (distance < this.eps) { - // 把目标点存下来作为新的头 - this.cells[0] = this.next_cell; - // 下一步之前,将当前的 this.next_cell 清空 - this.next_cell = null; - - // 将状态改为 idle 表示当前停下的状态 - this.status = "idle"; + // render 不来自基类,需要本类自己实现 + render() { + // 取出单元格长度引用和画布 + const L = this.gamemap.L; + const ctx = this.gamemap.ctx; - // 如果蛇不边长,则每次移动时,增加头部的同时,把蛇尾砍掉 - if (!this.check_tail_increasing()) { - this.cells.pop(); - } - } - // 不重合表示尚未移动到下一步,还可以继续移动 - else { - // 按时间(second)定义移动距离: 每一帧走过的距离 = 速度 * 两帧时间间隔 / 1000(ms) - const move_distance = this.speed * this.timedelta / 1000; // 处以 1000 ,将毫秒单位转换成秒单位 - this.cells[0].x += move_distance * dx / distance; - this.cells[0].y += move_distance * dy / distance; - - // 更新蛇尾位置 - if(!this.check_tail_increasing()) { - const k = this.cells.length; - // 取出当前的蛇尾 - const tail = this.cells[k - 1]; - // 当前蛇尾的下一个目标位置 - const tail_target = this.cells[k - 2]; - - // 把当前蛇尾移动到下一个蛇尾目标位置: 将 tail 移动到 tail_target 位置 - // 求两个位置的横纵坐标差值 - const tail_dx = tail_target.x - tail.x; - const tail_dy = tail_target.y - tail.y; - - // 移动蛇尾 - tail.x += move_distance * tail_dx / distance; - tail.y += move_distance * tail_dy / distance; - } - } + // 定义填充颜色 + ctx.fillStyle = this.color; + // 当蛇已经去世了,将其颜色变成惨白色 + if (this.status === "die") { + ctx.fillStyle = "#ffffff"; } - // update 方法每一帧执行一次,每秒钟执行 60 次 - update() { - // 当蛇处于 move 移动状态时, 使用 update_move 方法更新蛇的移动 - if (this.status === "move") { - // 每一帧调用 this.update_move() - this.update_move(); - } + // 蛇的身体不止一节,需要枚举画出所有的肢结。of 遍历 cells 值 + for (const cell of this.cells) { + // 画成正方形 + // ctx.fillRect(cell.c * L, cell.r * L, L, L); - // 每次更新蛇类对象都重新调用一次渲染 - this.render(); + // 画成圆形 + ctx.beginPath(); + ctx.arc(cell.x * L, cell.y * L, L * 0.5 * 0.8, 0, 2 * Math.PI); + // 填充颜色 + ctx.fill(); } - // render 不来自基类,需要本类自己实现 - render() { - // 取出单元格长度引用和画布 - const L = this.gamemap.L; - const ctx = this.gamemap.ctx; + // 使得蛇身体丰满一点 + for (let i = 1; i < this.cells.length; i++) { + const a = this.cells[i - 1], + b = this.cells[i]; + // 当两个目标点重合时,不用在绘制矩形填充 + if (Math.abs(a.x - b.x) < this.eps && Math.abs(a.y - b.y) < this.eps) + continue; - // 定义填充颜色 - ctx.fillStyle = this.color; - // 当蛇已经去世了,将其颜色变成惨白色 - if (this.status === "die") { - ctx.fillStyle = "#ffffff"; - } - - // 蛇的身体不止一节,需要枚举画出所有的肢结。of 遍历 cells 值 - for(const cell of this.cells){ - // 画成正方形 - // ctx.fillRect(cell.c * L, cell.r * L, L, L); - - // 画成圆形 - ctx.beginPath(); - ctx.arc(cell.x * L, cell.y * L, L * 0.5 * 0.8, 0, 2*Math.PI); - // 填充颜色 - ctx.fill(); - } - - // 使得蛇身体丰满一点 - for (let i = 1; i < this.cells.length; i++) { - const a = this.cells[i - 1], b = this.cells[i]; - // 当两个目标点重合时,不用在绘制矩形填充 - if (Math.abs(a.x - b.x) < this.eps && Math.abs(a.y - b.y) < this.eps) - continue; - - // 定义蛇身结点宽度比例 - const snake_node_width = 0.7; - // 如果两个目标点在竖方向重合(横坐标一致,纵坐标不重合)时的画法 - if (Math.abs(a.x - b.x) < this.eps) { - ctx.fillRect((a.x - snake_node_width / 2) * L, Math.min(a.y, b.y) * L, L * snake_node_width, Math.abs(a.y - b.y) * L); - } - // 横方向的画法 - else { - ctx.fillRect(Math.min(a.x, b.x) * L, (a.y - snake_node_width / 2) * L, Math.abs(a.x - b.x) * L, L * snake_node_width); - } - } - - // 绘制蛇眼睛 - // 定义眼睛颜色 - ctx.fillStyle = "#fbff00"; - // 定义蛇眼睛大小 - const snake_eye_size = 0.07 * L; - // 画眼睛 - for (let i = 0; i < 2; i ++) { - // 眼睛的横纵坐标(乘以 L 是绝对距离) - const eye_x = (this.cells[0].x + this.eye_dx[this.eye_direction][i] * 0.15) * L; - const eye_y = (this.cells[0].y + this.eye_dy[this.eye_direction][i] * 0.15) * L; - - ctx.beginPath() - ctx.arc(eye_x, eye_y, snake_eye_size, 0, 2 * Math.PI); - ctx.fill(); - } + // 定义蛇身结点宽度比例 + const snake_node_width = 0.7; + // 如果两个目标点在竖方向重合(横坐标一致,纵坐标不重合)时的画法 + if (Math.abs(a.x - b.x) < this.eps) { + ctx.fillRect( + (a.x - snake_node_width / 2) * L, + Math.min(a.y, b.y) * L, + L * snake_node_width, + Math.abs(a.y - b.y) * L + ); + } + // 横方向的画法 + else { + ctx.fillRect( + Math.min(a.x, b.x) * L, + (a.y - snake_node_width / 2) * L, + Math.abs(a.x - b.x) * L, + L * snake_node_width + ); + } } -} \ No newline at end of file + + // 绘制蛇眼睛 + // 定义眼睛颜色 + ctx.fillStyle = "#fbff00"; + // 定义蛇眼睛大小 + const snake_eye_size = 0.07 * L; + // 画眼睛 + for (let i = 0; i < 2; i++) { + // 眼睛的横纵坐标(乘以 L 是绝对距离) + const eye_x = + (this.cells[0].x + this.eye_dx[this.eye_direction][i] * 0.15) * L; + const eye_y = + (this.cells[0].y + this.eye_dy[this.eye_direction][i] * 0.15) * L; + + ctx.beginPath(); + ctx.arc(eye_x, eye_y, snake_eye_size, 0, 2 * Math.PI); + ctx.fill(); + } + } +} diff --git a/web/src/assets/scripts/Wall.js b/web/src/assets/scripts/Wall.js index b62cfab..611f992 100644 --- a/web/src/assets/scripts/Wall.js +++ b/web/src/assets/scripts/Wall.js @@ -2,35 +2,34 @@ import { AcGameObject } from "./AcGameObject"; export class Wall extends AcGameObject { - // 构造函数定义,参数为墙的坐标 r 行, c 列,gamemap 用于绘制 - constructor(r,c,gamemap){ - // 先执行基类的构造函数 - super(); + // 构造函数定义,参数为墙的坐标 r 行, c 列,gamemap 用于绘制 + constructor(r, c, gamemap) { + // 先执行基类的构造函数 + super(); - this.r = r; - this.c = c; - this.gamemap = gamemap; - this.color = "#b47226"; - } + this.r = r; + this.c = c; + this.gamemap = gamemap; + this.color = "#b47226"; + } - // 墙的更新 - update(){ - // 执行渲染 - this.render(); - } + // 墙的更新 + update() { + // 执行渲染 + this.render(); + } - // 墙的渲染 - render(){ - // 从 gamemap 对象中拿到小格(墙)的边长 - const L = this.gamemap.L; + // 墙的渲染 + render() { + // 从 gamemap 对象中拿到小格(墙)的边长 + const L = this.gamemap.L; - // 拿到ctx画布 - this.ctx = this.gamemap.ctx; + // 拿到ctx画布 + this.ctx = this.gamemap.ctx; - // 设置 ctx 画布填充色 - this.ctx.fillStyle = this.color; - // 绘制矩形 - this.ctx.fillRect(this.c*L, this.r*L, L, L); - - } -} \ No newline at end of file + // 设置 ctx 画布填充色 + this.ctx.fillStyle = this.color; + // 绘制矩形 + this.ctx.fillRect(this.c * L, this.r * L, L, L); + } +} diff --git a/web/src/router/index.js b/web/src/router/index.js index e263b3e..1cbb329 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -1,67 +1,105 @@ -import { createRouter, createWebHistory } from 'vue-router' +import { createRouter, createWebHistory } from "vue-router"; // 导入所有 view 页面 -import PkIndexView from '../views/pk/PkIndexView.vue' -import RanklistIndexView from '../views/ranklist/RanklistIndexView.vue' -import RecordIndexView from '../views/record/RecordIndexView.vue' -import UserBotIndexView from '../views/user/bot/UserBotIndexView.vue' -import NotFound from '../views/error/NotFound.vue' -import UserAccountLoginView from '@/views/user/account/UserAccountLoginView.vue' -import UserAccountRegisterView from '@/views/user/account/UserAccountRegisterView.vue' - +import PkIndexView from "../views/pk/PkIndexView.vue"; +import RanklistIndexView from "../views/ranklist/RanklistIndexView.vue"; +import RecordIndexView from "../views/record/RecordIndexView.vue"; +import UserBotIndexView from "../views/user/bot/UserBotIndexView.vue"; +import NotFound from "../views/error/NotFound.vue"; +import UserAccountLoginView from "@/views/user/account/UserAccountLoginView.vue"; +import UserAccountRegisterView from "@/views/user/account/UserAccountRegisterView.vue"; +// 读入 store 信息 +import store from "../store/index"; // 定义所有页面的 URL 路由 const routes = [ { - path:'/', - name:'home', + path: "/", + name: "home", // 重定向:将 home 重定向到 pk 页面 - redirect:'/pk/' + redirect: "/pk/", + // meta 存其他信息 + meta: { + // 页面是否需要授权 + requestAuth: true, + }, }, { - path:'/pk/', - name:'pk_index', - component:PkIndexView + path: "/pk/", + name: "pk_index", + component: PkIndexView, + meta: { + requestAuth: true, + }, }, { - path:'/ranklist/', - name:'ranklist_index', - component:RanklistIndexView + path: "/ranklist/", + name: "ranklist_index", + component: RanklistIndexView, + meta: { + requestAuth: true, + }, }, { - path:'/record/', - name:'record_index', - component:RecordIndexView + path: "/record/", + name: "record_index", + component: RecordIndexView, + meta: { + requestAuth: true, + }, }, { - path:'/user/bot/', - name:'user_bot_index', - component:UserBotIndexView + path: "/user/bot/", + name: "user_bot_index", + component: UserBotIndexView, + meta: { + requestAuth: true, + }, }, { - path:'/user/account/login/', - name:'user_account_login', - component:UserAccountLoginView + path: "/user/account/login/", + name: "user_account_login", + component: UserAccountLoginView, + meta: { + requestAuth: false, + }, }, { - path:'/user/account/register/', - name:'user_account_register', - component:UserAccountRegisterView + path: "/user/account/register/", + name: "user_account_register", + component: UserAccountRegisterView, + meta: { + requestAuth: false, + }, }, { - path:'/404/', - name:'404', - component:NotFound + path: "/404/", + name: "404", + component: NotFound, + meta: { + requestAuth: false, + }, }, { // 正则匹配所有其他非法页面到 404 - path:'/:catchAll(.*)', - redirect:'/404/' - } -] + path: "/:catchAll(.*)", + redirect: "/404/", + }, +]; const router = createRouter({ history: createWebHistory(), - routes -}) + routes, +}); -export default router \ No newline at end of file +// to 表示从那个页面跳转, from 表示从哪个页面跳转出去, next 表示页面要不要执行下一步操作 +router.beforeEach((to, from, next) => { + // 如果页面需要授权而且未登录,则跳转到用户登录页面 + if (to.meta.requestAuth && !store.state.user.is_login) { + next({ name: "user_account_login" }); + } else { + // next() 跳转到默认页面 + next(); + } +}); + +export default router; diff --git a/web/src/store/index.js b/web/src/store/index.js index 647fec4..56bbab3 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -1,17 +1,12 @@ -import { createStore } from 'vuex' -import ModuleUser from './user' +import { createStore } from "vuex"; +import ModuleUser from "./user"; export default createStore({ - state: { - }, - getters: { - }, - mutations: { - }, - actions: { - }, + state: {}, + getters: {}, + mutations: {}, + actions: {}, modules: { user: ModuleUser, - } -}) - + }, +}); diff --git a/web/src/store/user.js b/web/src/store/user.js index 26471e6..206d179 100644 --- a/web/src/store/user.js +++ b/web/src/store/user.js @@ -1,100 +1,93 @@ -import $ from 'jquery' +import $ from "jquery"; export default { - state: { - id: "", - username: "", - photo: "", - token: "", - is_login: false, + state: { + id: "", + username: "", + photo: "", + token: "", + is_login: false, + }, + getters: {}, + // 同步事件 + mutations: { + // 更新用户信息 + updateUser(state, user) { + state.id = user.id; + state.username = user.username; + state.photo = user.photo; + state.is_login = user.is_login; }, - getters: { - + // 更新用户 Token + updateToken(state, token) { + state.token = token; }, - // 同步事件 - mutations: { - // 更新用户信息 - updateUser(state, user){ - state.id = user.id; - state.username = user.username; - state.photo = user.photo; - state.is_login = user.is_login; + // 退出登录 + logout(state) { + state.id = ""; + state.username = ""; + state.photo = ""; + state.token = ""; + state.is_login = false; + }, + }, + // 异步事件 + actions: { + // 登录函数 + login(context, data) { + $.ajax({ + url: "http://localhost:3000/user/account/token/", + type: "POST", + data: { + username: data.username, + password: data.password, }, - // 更新用户 Token - updateToken(state, token){ - state.token = token; - }, - // 退出登录 - logout(state){ - state.id = ""; - state.username = ""; - state.photo = ""; - state.token = ""; - state.is_login = false; - } - }, - // 异步事件 - actions: { - // 登录函数 - login(context, data){ - $.ajax({ - url: "http://localhost:3000/user/account/token/", - type: "POST", - data: { - username: data.username, - password: data.password, - }, - success(resp) { - // console.log(resp.token, "\n成功了\n", resp.error_message); - if(resp.error_message === "success"){ - /* + success(resp) { + // console.log(resp.token, "\n成功了\n", resp.error_message); + if (resp.error_message === "success") { + /* 登录成功则将获取到的 resp 里的 token 传给 mutations 里的 updateToken 方法,对用户 token 信息进行更新 */ - context.commit("updateToken", resp.token); - data.success(resp); - } else { - data.error(resp); - } - - }, - error(resp) { - data.error(resp); - }, - }); + context.commit("updateToken", resp.token); + data.success(resp); + } else { + data.error(resp); + } }, - // 获取登录成功后的用户信息 - getinfo(context,data){ - $.ajax({ - url: "http://localhost:3000/user/account/info/", - type: "GET", - headers: { - Authorization: - "Bearer " + context.state.token, - }, - success(resp) { - if(resp.error_message === "success"){ - // 更新用户信息 - context.commit("updateUser",{ - ...resp, // 解构 resp 中的内容 - is_login: true, // 登录成功,将 is_login 置为 true - }); - data.success(resp); // 调用回调函数 - } else { - data.error(resp); - } - }, - error(resp) { - data.error(resp); - }, - }); + error(resp) { + data.error(resp); }, - logout(context){ - context.commit("logout"); - } - + }); }, - modules: { - - } -} \ No newline at end of file + // 获取登录成功后的用户信息 + getinfo(context, data) { + $.ajax({ + url: "http://localhost:3000/user/account/info/", + type: "GET", + headers: { + Authorization: "Bearer " + context.state.token, + }, + success(resp) { + if (resp.error_message === "success") { + // 更新用户信息 + context.commit("updateUser", { + ...resp, // 解构 resp 中的内容 + is_login: true, // 登录成功,将 is_login 置为 true + }); + data.success(resp); // 调用回调函数 + } else { + data.error(resp); + } + }, + error(resp) { + data.error(resp); + }, + }); + }, + logout(context) { + context.commit("logout"); + }, + }, + modules: {}, +};