谷粒学院16万字笔记+1600张配图(五)——讲师管理前端

项目源码与所需资料
链接:https://pan.baidu.com/s/1azwRyyFwXz5elhQL0BhkCA?pwd=8z59
提取码:8z59

文章目录

demo05-讲师管理前端

1.搭建项目前端页面环境

我们这里用vue-admin-template框架来进行环境搭建

1.去资料中找到模板压缩文件,有两个,我们使用171KB的那个模板压缩文件

vue-admin-template和vue-element-admin的区别:前者是精简版,只有最基础的功能,后者功能更多。我们在前者的基础上进行二次开发,把后者当做工具箱,想要什么功能或者组件就去后者那里复制过来

在这里插入图片描述

2.解压这个171KB的压缩文件到工作区里面(我的工作区是vs1010)

在这里插入图片描述

3.在vscode中以终端方式打开上一步解压得到的文件夹,使用命令npm install进行依赖安装,安装完毕后会自动生成node_modules文件夹,这个文件夹存放的就是安装好的依赖

在这里插入图片描述

扫描二维码关注公众号,回复: 15507028 查看本文章

在这里插入图片描述

如果下载时爆红,那么此时自动生成的node_modules文件夹里面只会安装部分依赖,并没有将package.json中记录的依赖全都按装,所以我们要删掉这个自动生成的node_modules文件夹,重新执行npm install命令安装依赖,通常多试几次就能下载成功了

4.上面只是为了让我们熟悉一下流程,其实老师给的有已下载好依赖的后台系统页面,我放在了资料中(苹果电脑不用看4、5步了,因为苹果电脑只能使用npm install命令自己下载依赖,没办法使用已经下载好的依赖)

在这里插入图片描述

5.将这个压缩文件解压到工作区中(我的工作区是vs1010)

在这里插入图片描述

6.在终端中使用如下命令启动项目,启动后的项目的默认端口号是9528

npm run dev

在这里插入图片描述

7.在地址栏输入http://localhost:9528就可以访问了

在这里插入图片描述

2.项目前端页面框架介绍

2.1项目入口

我们编写的后端程序的入口是main方法,同样的这个前端框架也有入口(有两个入口)

入口一:vue-admin-1010–>index.html

不过这个页面只做了一件事:写一个div标签,并给该div标签添加一个id属性。做这件事的意义在讲解入口二时会说到

在这里插入图片描述

入口二:vue-admin-1010–>src–>main.js

该js中先是用import...from...引入了其它组件或者文件,最后new Vue({el: 'app',...}),其中app就是入口一的index.html中div标签的id=“app”

所以呢,入口一(index.html)写一个div标签用于显示,入口二(main.js)引入了其它组件然后创建vue对象进行操作

在这里插入图片描述

2.2框架使用的技术

我们前端页面环境使用的vue-admin-template框架(模板)主要基于两种技术实现出来。

vue-admin-template模板 = vue + element-ui

2.3 项目的目录结构

1.build目录:放项目构建的脚本文件(一般不用管这个目录)

2.config目录:基本配置信息

我们可以在index.js中修改ip、端口号(可以修改但没必要修改),但注意一定要把index.js中useEslint: true改为useEslint: false(这是ESLint插件,这个插件用来检查代码,但是它的检查太严格了,我多敲几个空格多敲几个换行就会被视为错误代码,所以我们改为false,不使用这个插件)

在这里插入图片描述

我们在"1.搭建项目前端页面环境"的第6步使用命令npm run dev启动项目时会执行文件dev.env.js(同理如果使用命令npm run prod启动项目时会执行文件prod.env.js)

dev.env.js和prod.env.js都有BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"',这表示要访问的后端接口的地址,这里是默认地址,我们日后(3.1修改前端项目访问路径的第4步)要改成我们本地的后端接口地址localhost:8001

在这里插入图片描述

3.node_modules目录:存放下载的依赖

4.static目录:存放静态资源(一般不用管这个目录)

5.src目录:

①api目录:定义方法

②assets目录:存放静态资源(如:css文件、js文件、项目的相关图片…)

③components目录:存放一些组件/插件。比如说,我觉得当前这个框架缺少某个功能,那么我就可以引入额外的框架,引入的框架就放到这个目录(components)

④icons目录:存放项目用到的图标

⑤router目录:路由,就是我们说的菜单

⑥store目录:存放脚本文件(一般不用管这个目录)

⑦styles目录:存放样式文件(一般不用管这个目录)

⑧utils目录:存放框架中用到的工具类(一般不用管这个目录)

⑨views目录:存放项目具体的页面

2.4如何使用框架

我们用框架的目的是少写代码,更快的完成功能开发,那么理所当然的我们使用的这个前端页面框架也给我们封装了很多功能以便让我们减少代码编写(比如①封装了ajax请求、②以前必须使用babel将es6模块化转为es5模块化才可以在node中执行,现在框架会替我们完成es6转为es5)

那么我们开发功能时就需要做什么,具体流程是什么呢(这里先混眼熟就行,在后面的"4.4总结"中会细说)

  • 在src目录的api目录下定义方法
  • 在router目录的index.js文件中写路由
  • 在views目录的页面中调用方法,在页面中使用element-ui做显示

3.后台系统登录功能改造

3.1修改前端项目访问路径

1.在终端执行npm run dev命令来启动前端项目,启动成功后在地址栏输入http://localhost:9528进行访问

在这里插入图片描述

在这里插入图片描述

2.在打开的页面按F12打开开发者工具,在开发者工具中点击"Network"菜单,然后点击页面的"Sign in"按钮(点击按钮可能登录不进去,这是因为要访问的这个网站经常崩掉,不用管,第4步我们会把访问路径改为我们自己开发的后端接口的路径localhost:8001)

在这里插入图片描述

3.我们点击发送的第一个请求,可以看到请求路径是:

https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin/user/login

这时我们回过头看"2.3 项目的目录结构"的第2条说的config目录下的dev.env.js和prod.env.js页面共有的BASE_API:

https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin

发现第一个路径前半部分和第二个路径一样,后半部分多了个"/user/login",此时我们也就明白了:原来前端项目要访问的后端接口的地址就是在config目录下的dev.env.js和prod.env.js页面中的BASE_API设置的呀

在这里插入图片描述

4.分析完了现在开始修改后台访问路径:

将config目录下的dev.env.js中的BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"',注释掉,并添加如下这行代码

BASE_API: '"http://localhost:8001"',

在这里插入图片描述

3.2稍微看一点点底层

1.看src–>utils–>request.js:

在这里插入图片描述

因为人家框架定义了非20000的状态码都是抛错,所以我们在"demo03-后台讲师管理模块"的"5.1统一返回数据格式"的第2步定义成功的状态码是20000

2.看src–>api–>login.js:

在这里插入图片描述

我们看login.js定义的方法中的这两个:login方法和getInfo方法:

  • 这两个方法的返回值都是request(…)方法,包括以后在api目录下定义的所有方法我猜测返回值都是request(…)方法,因为在request(…)方法中框架给我们封装了axios异步请求(这里不细说了)
  • url: '/user/login'url: '/user/info'就是axios要访问的路径,所以我们等下需要在后端的控制层中编写两个方法,使访问/user/login/user/info时可以执行对应的方法

3.看src–>store–>modules–>user.js:

在这里插入图片描述

登录账号时底层会做两件事:登录&获取用户信息。

看"登录"方法中,成功调用login方法(在刚刚的第2条说过这个方法,这是在api目录下定义的方法)后要做的事在then(response => {…})里面编写了,省略号中有部分代码是:const data = response.datacommit('SET_TOKEN', data.token),从这里我们可以看出来后端对应方法返回的数据中一定要有token。同理,看"获取用户信息"方法,我们也知道了后端对应方法返回的数据中一定要有roles、name、avatar

3.3在后端开发接口

在service_edu模块的控制层创建一个EduLoginController类,并在类中编写代码

@RestController
@RequestMapping("/eduservice/user")
public class EduLoginController {
    
    

    //登录
    @PostMapping("login")
    public R login() {
    
    
        return R.ok()
                .data("token", "admin");
    }

    //获取用户信息
    @GetMapping("info")
    public R info() {
    
    
        return R.ok()
                .data("roles", "[admin]")
                .data("name", "admin")
                .data("avatar", "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
    }
}

在这里插入图片描述

  • login方法中的return R.ok().data("token", "admin");:我们在"3.2稍微看一点点底层"的第3条已经详细解释过了为什么返回的数据需要有token。info方法中的 return R.ok().data("roles", "[admin]").da......也是同理
  • 我们现在先来模拟登录,也就是不和数据库挂钩,所以:①数据啥的都是我们自己捏造的②实际开发中这两个方法肯定都会有参数,但我们现在只是模拟,先不写参数

3.4检查login.js有哪里需要修改

1.因为我们在"3.3在后端开发接口"编写代码时设置路径是/eduservice/user/login/eduservice/userinfo,所以我们要去src–>api–>login.js中将login方法中的url: '/user/login'改为url: '/eduservice/user/login';将getInfo方法中的url: '/user/info'改为url: '/eduservice/user/info'

2.我们在"3.3在后端开发接口"编写的login方法上有一个注解是@PostMapping; info方法上有一个注解是@GetMapping,那么就需要让login.js页面的login方法中的method是post;让getInfo方法中的method是get。恰好这两个方法的method本来就是这样的,所以不需要修改

3.我们在"3.3在后端开发接口"编写的login方法和info方法都没有参数,所以login.js页面的login方法中的data: {...}和getInfo方法中的params: {...}暂时都无实际意义,暂时用不上,但也不用管它们,就在这儿放着就可以了

所以说呢,login.js页面中只需要修改login和getInfo方法的url,别的地方不需要修改。修改后的login.js如下:

在这里插入图片描述

3.5测试&跨域问题

1.启动后端项目

在这里插入图片描述

2.在终端使用命令npm run dev启动前端项目

在这里插入图片描述

3.在地址栏输入http://localhost:9528进入登录页面,然后按F12打开开发者工具,接着点击"Sign in"按钮发现还是不能登录,我们点击"Console"菜单去控制台看一下输出了什么错误信息

在这里插入图片描述

No 'Access-Control-Allow-Origin’这在术语上是叫跨域问题

什么叫跨域:通过一个地址去访问另外一个地址,这个过程中如果①访问协议(http、https)、②ip地址、③端口号这三处只要有任何一处不一样,就叫做跨域

我们现在是通过http://localhost:9528去访问http://localhost:8001,这两个地址的端口号不一样,所以会出现跨域问题

4.跨域问题的解决方式有很多种,简单列举两种:

  • 在后端接口controller上添加注解@CrossOrigin(常用)
  • 使用网关解决(后面会讲到)

我这里就是使用添加注解来解决跨域问题的:

在这里插入图片描述

在这里插入图片描述

5.重启后端项目,在地址栏输入http://localhost:9528进入登录页面,然后按F12打开开发者工具,接着点击"Sign in"按钮,此时就可以正常登录进去了。

并且注意看,它既向.../user/login发送了请求,又向.../user/info发送了请求,这就证实了我们在"3.2稍微看一点点底层"中第3点说的话:登录账号时底层会做两件事:登录&获取用户信息。

在这里插入图片描述

在这里插入图片描述

可能有朋友会有疑问:为啥每个请求都是请求了两次呢?

这是浏览器的机制,就拿第一个请求login举例:

该路径的第一次请求的请求方式是options;第二次请求的请求方式是post。

第一次的请求可以理解为是一个预请求,只是为了测是否能和后台服务器接口连通,如果能连通才会进行第二次的真正请求,不过第一次请求不会返回任何数据。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.前端框架开发过程介绍

4.1在src–>router–>index.js中添加路由

在这里插入图片描述

怎么编写添加路由的代码呢?我们可以看一下Example菜单的示例代码,以后路由就根据这个示例代码修改就可以了

在这里插入图片描述

在这里插入图片描述

component: () => import(...) 就是路由对应的页面,这样的话,点击某个路由,就可以显示路由对应的页面内容

4.2在src–>views中创建vue页面

既然点击路由可以显示路由对应的页面内容,那么相应的我们就需要创建页面

1在哪创建vue页面:

看上面那张图中的component: () => import('@/views/table/index'),我们根据路径可以找到view–>table–>index

在这里插入图片描述

所以我们就知道了,我们创建的页面是在views目录下(准确来说,是views目录的子目录下)

我们上面说的页面存放位置是框架默认的,当然了,你说我不想按照框架默认的存放位置(先在views目录下创建子目录,然后在子目录下创建页面),我想在views目录下创建一个子目录,然后在子目录下再创建一个子目录,最后才在最里层的目录中创建vue页面,这当然是可以的,但是别忘了需要配套地填写router–>index.js页面的component: () => import(...)中的路径。反正页面创建在哪都可以(大前提是在src目录下),只要配套填写路径就可以了

2.为什么创建的是vue页面而不是html页面或其他页面?

看上面那张图,人家创建的就是vue页面

4.3在vue页面中编写功能

老规矩,先看人家怎么在页面中编写代码的:

1.看src–>views–>table–>index.vue页面中的import { getList } from '@/api/table',这我们在"demo04-前端技术"的"9.3.1第一种写法"说过,是引入js文件中定义的方法

在这里插入图片描述

2.我们根据import { getList } from '@/api/table'可以去api目录下找到table.js文件,发现这个js文件是用来定义方法的。这样我们也就清楚了下一步应该干嘛:在api目录下创建js文件,定义方法

在这里插入图片描述

3.回到最初的问题:我们需要在vue页面中编写什么代码?

先再看一下vue页面:

在这里插入图片描述

现在我们知道vue页面就需要编写什么代码了吧:先使用import...from...引入方法,然后编写data():{...},created(){...},methods:{...},最后用element-ui显示数据内容(就在该页面的1-43行,太多了,所以我就没有放到截图里面)

4.4总结

  • 在src–>router–>index.js中添加路由
  • 在src–>views中创建vue页面
  • 在src–>在api目录下创建js文件,定义方法
  • 在创建的vue页面中编写代码
    • 引入api目录下定义的方法
    • 编写data():{...},created(){...},methods:{...}
    • 用element-ui显示数据内容

5.讲师列表

5.1在src–>router–>index.js中添加路由

1.随便找一段例子的代码,将代码复制粘贴

在这里插入图片描述

下面是复制粘贴好的indes.js页面:

在这里插入图片描述

2.将我们刚刚复制粘贴过来的代码根据需要进行修改,修改后务必保存修改

在这里插入图片描述

3.如果后端、前端项目都已启动,那么在地址栏输入http://localhost:9528进行访问,在登录页面点击"Sign in"登录进去后效果如下:

在这里插入图片描述

5.2创建路由对应的页面

我们在"4.2在src–>views中创建vue页面"中说过,我们可以在任何地方(大前提是在src目录下)创建vue页面,只要配套的修改router–>index.js页面的component: () => import(...)中的路径即可

1.那么我们就可以在src–>views目录下创建一个目录edu,在edu下再创建一个目录teacher,最后在teacher目录下创建页面list.vue和save.vue

在这里插入图片描述

2.配套的修改index.js中component: () => import(...)的路径,修改后记得保存修改

在这里插入图片描述

3.在list.vue中编写代码

<template>
  <div class="app-container">
    讲师列表
  </div>
</template>

在这里插入图片描述

<template><div class="app-container">......</div></template>在我们以后创建的所有vue页面中都是必不可少的,这是人家封装的,省略号的内容我们根据具体需求写

4.在save.vue中编写代码

<template>
  <div class="app-container">
    讲师添加
  </div>
</template>

在这里插入图片描述

5.3在api目录下创建js文件,定义方法

1.在api目录下创建teacher文件夹,然后在该文件夹下创建teacher.js文件

在这里插入图片描述

2.老办法,参考框架怎么定义方法的:将api–>tables.js中的代码复制粘贴到teacher.js中

在这里插入图片描述

有人可能要问:return request({...})中的request方法是干嘛用的?

这是框架给我们做的封装,只要我们调用了getList方法,就会使用axios发送ajax请求

3.es6模块化操作有两种写法,框架给的是第一种写法,我们开发中用第二种写法("demo04-前端技术"的9.3.2第二种写法)比较多,所以我们对teacher.js的代码进行修改

import request from '@/utils/request'

export default {
    
    
    getList(params) {
    
    
        return request({
    
    
          url: '/table/list',
          method: 'get',
          params
        })
      }
}

在这里插入图片描述

4.好,我们现在根据需要来修改定义方法

先看我们后端控制层的EduTeacherController类和类中的方法:条件查询讲师带分页(pageTeacherCondition)

在这里插入图片描述

在这里插入图片描述

我们需要知道这几个东西:

  • EduTeacherController类的路径是/eduservice/edu-teacher
  • pageTeacherCondition方法的路径是pageTeacherCondition/{current}/{limit}
  • pageTeacherCondition方法的参数是current, limit, teacherQuery。分别表示当前页、每页记录数、条件对象
  • pageTeacherCondition方法的请求方式是post
  • pageTeacherCondition方法的第三个参数用@RequestBody注解修饰,所以前端传过来的第三个参数需要是json数据(忘了这个注解的可以去"demo03-后台讲师管理模块"的"7.5改进"回顾一下)

知道了上面这些,我们就可以修改teacher.js中定义的方法了:

  • url的参数需要修改,有两种方式(建议用第一种)

    • url: ‘/eduservice/edu-teacher/pageTeacherCondition/’ + current + “/” + limit
    • url: `/eduservice/edu-teacher/pageTeacherCondition/${current}/${limit}`
      • 注意,这不是单引号,是飘号(在键盘"Esc"的下面)
  • 方法参数改为current, limit, teacherQuery。分别表示当前页、每页记录数、条件对象

  • 请求方式改为post

  • 删去"params",添加一行代码data:teacherQuery,这表示把teacherQuery对象转换为json数据传递到接口中

  • 方法名改为getTeacherListPage

5.修改后的teacher.js完整代码如下:

import request from '@/utils/request'

export default {
    
    
  //1.讲师列表(条件查询带分页)
  //current:当前页  limit:每页记录数  teacherQuery:条件对象
  getTeacherListPage(current, limit, teacherQuery) {
    
    
    return request({
    
    
      url: `/eduservice/edu-teacher/pageTeacherCondition/${
      
      current}/${
      
      limit}`,
      method: 'post',
      //data: teacherQuery表示把teacherQuery对象转换为json数据传递到接口中
      data: teacherQuery
    })
  }
}

在这里插入图片描述

5.4在list.vue页面调用5.3中定义的方法

1.既然用人家的框架,就要用人家规定的格式编写代码,参考人家给的src–>views–>dashboard–>index.vue页面我们可以知道,需要先写一个<script>export default {...}</script>,然后才能在省略号的位置编写我们的核心代码

在这里插入图片描述

2.在src–>views–>edu–>teacher–>list.vue页面先敲一个<script人家就会给提示

在这里插入图片描述

在这里插入图片描述

3.将src–>api下的teacher文件夹改名为edu(没有任何逻辑,只是因为老师这里想改名,那我也跟着改)

在这里插入图片描述

4.需要先在页面中引入teacher.js文件

import teacher from '@/api/edu/teacher'

在这里插入图片描述

5.核心代码是有固定结构的,我们在"demo04-前端技术"的"4.axios"的第5步说过固定结构,只是框架给我们进行了封装,不再需要我们自己创建vue对象,下面我们写出核心代码的固定结构

//编写核心代码
// data: {
    
    

// },
data() {
    
     //定义变量和初始值
  return {
    
    

  }
},
created() {
    
     //页面渲染之前执行,一般用来调用methods中定义的方法

},
methods: {
    
     //创建具体的方法,调用teacher.js中定义的方法

}

在这里插入图片描述

其中data: {...}这种写法没问题,不过我们习惯用第二种写法:data() {return {...}}解释一下这种写法:data是一个方法,我们在方法中加一个return,在return中写内容。

emmmm,离谱的是,弹幕有朋友说第一种写法是错误的,只能用第二种,我这里不深究了,反正我用的就是第二种

6.完整的teacher.vue代码如下:

<template>
  <div class="app-container">
    讲师列表
  </div>
</template>

<script>
//引入teacher.js文件
import teacher from '@/api/edu/teacher'

export default {
  //编写核心代码
  // data: {

  // },
  data() { //定义变量和初始值
    return {
      list: null, //查询之后接口返回的数据赋值给list
      page: 1, //当前页
      limit: 10, //每页记录数
      total: 0, //总记录数
      teacherQuery: {} //条件对象(我们这里设为空,也就是没条件,查询所有)
    }
  },
  created() { //页面渲染之前执行,一般用来调用methods中定义的方法
    //调用
    this.getList()
  },
  methods: { //创建具体的方法,调用teacher.js中定义的方法
    //讲师列表的方法
    getList() {
      //调用方法,使用axios发送ajax请求
      teacher.getTeacherListPage(this.page, this.limit, this.teacherQuery)
        .then(response => { //请求成功(response就是接口返回的数据)
          console.log(response)
        })
        .catch(error => {console.log(error)}) //请求失败
    }
  }
}
</script>

在这里插入图片描述

有人可能要问了,使用axios发送ajax请求不应该是getList() {axios.post("...").then(...).catch(...)}吗,为什么这里是teacher.getTeacherListPage(this.page, this.limit, this.teacherQuery).then(...).catch(...)

这是因为人家框架给我们做了封装,我们执行teacher.js中定义的getTeacherListPage方法的过程实际上就是使用axios发送ajax请求的过程(我们在"5.3在api目录下创建js文件,定义方法"的第2步就已经说过了)

5.5显示讲师列表

1.启动后端项目和前端项目,访问http://localhost:9528,在首页打开开发者工具,点击"讲师列表",查看控制台输出

在这里插入图片描述

2.上图中使用大方框圈起来的就是请求成功之后返回的信息,所以我们想要获取讲师列表就需要用这个代码:response.data.rows,知道了这些,那么list.vue中的console.log(response)就没什么用了,我们把它注释掉就可以了,然后我们给list.vue页面中添加如下代码:

this.list = response.data.rows
this.total = response.data.total
console.log(this.list)
console.log(this.total)

在这里插入图片描述

3.刷新页面,看控制台的输出:

在这里插入图片描述

4.现在我们已经可以拿到数据,接下来我们在list.vue页面的template标签的div标签下编写代码使数据以列表的形式在页面显示,这里我们使用element-ui组件实现

①进入element-ui的官网https://element.eleme.cn/#/zh-CN,点击组件的"查看详情"

在这里插入图片描述

②点击"Table 表格"就会有很多示例供我们选择,我们这里就使用第一个吧。点击"显示代码"

在这里插入图片描述

③为了便于观看,我们将示例的代码复制到文件中

在这里插入图片描述

  • :data="tableData"v-bing:data="tableData"的简写形式(忘了的可以去回顾"demo04-前端技术"的"3.3基本数据渲染和指令"),用来和tableData单向绑定以获取数据
  • element-ui给我们封装过了,底层会自动遍历数组,不再需要我们使用v-for来遍历数组
  • <el-table-column>标签的属性prop="date"和tableData中的date对应
  • <el-table-column>标签的属性label="..."就是表头

④老师给的有代码,我们直接复制粘贴到list.vue页面的template标签的div标签下

<!-- 表格 -->
<el-table
  v-loading="listLoading"
  :data="list"
  element-loading-text="数据加载中"
  border
  fit
  highlight-current-row>

  <el-table-column
    label="序号"
    width="70"
    align="center">
    <template slot-scope="scope">
      {
   
   { (page - 1) * limit + scope.$index + 1 }}
    </template>
  </el-table-column>

  <el-table-column prop="name" label="名称" width="80" />
  
  <el-table-column label="头衔" width="80">
    <template slot-scope="scope">
      {
   
   { scope.row.level===1?'高级讲师':'首席讲师' }}
    </template>
  </el-table-column>

  <el-table-column prop="intro" label="资历" />

  <el-table-column prop="gmtCreate" label="添加时间" width="160"/>

  <el-table-column prop="sort" label="排序" width="60" />

  <el-table-column label="操作" width="200" align="center">
    <template slot-scope="scope">
      <router-link :to="'/edu/teacher/edit/'+scope.row.id">
        <el-button type="primary" size="mini" icon="el-icon-edit">修改</el-button>
      </router-link>
      <el-button type="danger" size="mini" icon="el-icon-delete" @click="removeDataById(scope.row.id)">删除</el-button>
    </template>
  </el-table-column>
</el-table>

在这里插入图片描述

  • 我们在"5.5显示讲师列表"的第2步使用代码this.list = response.data.rows将讲师数据存放到了list变量中,所以截图的第6行是:data="list",以获取讲师数据
  • 截图第7行的element-loading-text="数据加载中" 作用是:点击前端页面的"讲师列表"后,如果网速比较慢,还没有获取到数据并展现,此时就会显示"数据加载中"。可有可无,如果不需要这个功能,就把截图中第5行的v-loading="listLoading"和第7行的element-loading-text="数据加载中"删掉,听弹幕说如果使用这个功能日后可能会报错,吓得我赶紧把这两行代码删了
  • 截图第17行的{ { (page - 1) * limit + scope.$index + 1 }}是显示序号,不用太深究,固定用就可以了
  • 截图第24行的<template slot-scope="scope">和第25行的{ { scope.row.level===1?'高级讲师':'首席讲师' }}
    • 因为我们数据库中存放的level是1和2,这里根据讲师的level是1还是2来给表格填充对应的等级(为什么这里是===而不是==呢:前者比较值和类型,后者只比较值)。
    • slot-scope="scope"是获取当前对象,scop.row用来获取行,scop.row.level用来获取行的level值。既然我们知道了这些,那么举一反三我们就可以写出和<el-table-column prop="name" label="名称" width="80" />作用一样的如下代码(但下面这段代码太冗余了,不建议这样写):
<el-table-column label="名称" width="80" >
	<template slot-scope="scope">
		{
   
   {scope.row.name}}
	</template>
</el-table-column>

⑤完整的list.vue代码如下

<template>
  <div class="app-container">
    <!-- 表格 -->
    <el-table
      :data="list"
      border
      fit
      highlight-current-row>

      <el-table-column
        label="序号"
        width="70"
        align="center">
        <template slot-scope="scope">
          {
   
   { (page - 1) * limit + scope.$index + 1 }}
        </template>
      </el-table-column>

      <el-table-column prop="name" label="名称" width="80" />

      <el-table-column label="头衔" width="80">
        <template slot-scope="scope">
          {
   
   { scope.row.level===1?'高级讲师':'首席讲师' }}
        </template>
      </el-table-column>

      <el-table-column prop="intro" label="资历" />

      <el-table-column prop="gmtCreate" label="添加时间" width="160"/>

      <el-table-column prop="sort" label="排序" width="60" />

      <el-table-column label="操作" width="200" align="center">
        <template slot-scope="scope">
          <router-link :to="'/edu/teacher/edit/'+scope.row.id">
            <el-button type="primary" size="mini" icon="el-icon-edit">修改</el-button>
          </router-link>
          <el-button type="danger" size="mini" icon="el-icon-delete" @click="removeDataById(scope.row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
//引入teacher.js文件
import teacher from '@/api/edu/teacher'

export default {
  //编写核心代码
  // data: {

  // },
  data() { //定义变量和初始值
    return {
      list: null, //查询之后接口返回的数据赋值给list
      page: 1, //当前页
      limit: 10, //每页记录数
      total: 0, //总记录数
      teacherQuery: {} //条件对象(我们这里设为空,也就是没条件,查询所有)
    }
  },
  created() { //页面渲染之前执行,一般用来调用methods中定义的方法
    //调用
    this.getList()
  },
  methods: { //创建具体的方法,调用teacher.js中定义的方法
    //讲师列表的方法
    getList() {
      //调用方法,使用axios发送ajax请求
      teacher.getTeacherListPage(this.page, this.limit, this.teacherQuery)
        .then(response => { //请求成功(response就是接口返回的数据)
          // console.log(response)
          this.list = response.data.rows
          this.total = response.data.total
          console.log(this.list)
          console.log(this.total)
        })
        .catch(error => {console.log(error)}) //请求失败
    }
  }
}
</script>

5.再次刷新页面进行测试,效果图如下

在这里插入图片描述

6.分页

6.1去element-ui找示例代码

点击分页组件"Paginaion 分页",有很多示例供我们选择

在这里插入图片描述

6.2使用老师给的代码

我们本来应该将某个示例的代码复制过来根据需求修改,但是老师已经给了我们修改之后的代码,如下。分页肯定是显示在列表的下面,所以我们将这段代码复制到<el-table>标签后面

<!-- 分页 -->
<el-pagination
:current-page="page"
:page-size="limit"
:total="total"
style="padding: 30px 0; text-align: center;"
layout="total, prev, pager, next, jumper"
@current-change="getList"
/>

在这里插入图片描述

  • :current-page="page":page-size="limit":total="total"分别表示得到当前页、每页记录数、总记录数,获取到这些值后element-ui底层给我们做了封装,会自己把分页的结构和功能展现出来(比如说:现在是在第几页,一共有几页,点击下一页会显示下一页的数据…)
  • @current-change="getList"v-on:current-change="getList"的简写(在"demo04-前端技术"的"3.5绑定事件"说过)这行代码表示每次做分页切换都会调用getList方法(该方法是我们在methods: {...}中定义的,该方法用来从后端获取分页数据和总记录数,并将其分别赋值给list变量和total变量)
    • 可以写为@current-change="getList()",这个()想写就写,不想写就不写

6.3修改getList方法

那么就出现了一个问题:我们定义的getList方法是没有参数的,并且分页代码中的:current-page="page":current-page和page做了单向数据绑定而不是双向数据绑定。那么getList方法内部每次执行teacher.getTeacherListPage(this.page, this.limit,......时的参数this.page永远都是我们在data() {...}中定义的page: 1。解决方法:

  • 给getList方法添加参数page = 1
    • 这样的话调用getList方法时如果不传参默认就是1(初次打开页面时,会在created() {…}中调用getList方法,此时就不给getList方法传参),如果传参就按照传的参数值
  • 并且给getList方法内部添加一行代码:this.page = page

有人可能会问了:既然我给getList方法添加了形参,那么在"6.2使用老师给的代码"的截图中的第50行的@current-change="getList"是不是要修改成@current-change="getList(page + 1)"?老师说不需要修改,说人家框架已经封装了,会自己传参数给getList方法

在这里插入图片描述

6.4测试

1.我们数据库中数据太少了,先直接去SQLyog中执行下面这些sql语句向我们的edu_teacher表中添加数据

INSERT INTO `edu_teacher` VALUES ('2', 'lucy', '高级讲师简介', '高级讲师资历', '2', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '2', '0', '2019-10-30 11:55:19', '2019-11-12 13:37:52');
INSERT INTO `edu_teacher` VALUES ('1189390295668469763', '李刚upupup2', '高级讲师简介111', '高级讲师111', '2', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '2', '0', '2019-10-30 11:55:19', '2019-11-12 13:37:52');
INSERT INTO `edu_teacher` VALUES ('1189426437876985858', '王二2', '高级讲师简介', '高级讲师', '1', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '0', '0', '2019-10-30 14:18:56', '2019-11-12 13:37:35');
INSERT INTO `edu_teacher` VALUES ('1189426464967995394', '王五2', '高级讲师简介', '高级讲师', '2', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '0', '0', '2019-10-30 14:19:02', '2019-11-12 13:37:18');
INSERT INTO `edu_teacher` VALUES ('1192249914833055747', '李四2', '高级讲师简介', '高级讲师', '1', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '0', '0', '2019-11-07 09:18:25', '2019-11-12 13:37:01');
INSERT INTO `edu_teacher` VALUES ('3', 'lucy2', '高级讲师简介', '高级讲师资历', '2', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '2', '0', '2019-10-30 11:55:19', '2019-11-12 13:37:52');
INSERT INTO `edu_teacher` VALUES ('1189390295668469764', '李刚upupup3', '高级讲师简介111', '高级讲师111', '2', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '2', '0', '2019-10-30 11:55:19', '2019-11-12 13:37:52');
INSERT INTO `edu_teacher` VALUES ('1189426437876985859', '王二3', '高级讲师简介', '高级讲师', '1', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '0', '0', '2019-10-30 14:18:56', '2019-11-12 13:37:35');
INSERT INTO `edu_teacher` VALUES ('1189426464967995395', '王五3', '高级讲师简介', '高级讲师', '2', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '0', '0', '2019-10-30 14:19:02', '2019-11-12 13:37:18');
INSERT INTO `edu_teacher` VALUES ('1192249914833055748', '李四3', '高级讲师简介', '高级讲师', '1', 'http://edu-longyang.oss-cn-beijing.aliyuncs.com/2020/08/05/25f411209c8b44b9b003482b6265c3c9file.png', '0', '0', '2019-11-07 09:18:25', '2019-11-12 13:37:01');

2.去页面中看效果

在这里插入图片描述

在这里插入图片描述

7.讲师条件查询

7.1去element-ui找示例代码

1.点击表单组件"Form 表单",有很多示例供我们选择,我们想要的效果是行内表单这个示例的效果,所以就点击行内表单示例的"显示代码"

在这里插入图片描述

2.为了便于观看,我们将示例的代码复制到文件中

在这里插入图片描述

  • 第1行的:inline="true"表示让表单在一行显示
  • 第1行的:model="formInline"v-bind:model="formInline"的简写,用来和formInline单向绑定以获取数据
  • 第3行的placeholder="审批人"和第6行的placeholder="活动区域"分别是示例中输入框和下拉框的提示词
  • 第3行的v-model="formInline.user"和第6行的v-model="formInline.region"双向数据绑定(在"demo04-前端技术"的"3.4单向、双向数据绑定"说过),这样的话我们在输入框和下拉框中填写的数据就会分别赋值给data() {...}中定义的变量user和region
  • 第一个是普通输入框,所以使用<el-input>标签;第二个是下拉框,所以使用<el-select>标签

7.2使用老师给的代码

1.我们本来应该将这些代码复制到list.vue中根据需求修改,但是老师已经给了我们修改之后的代码,如下。因为条件查询表单应该显示在讲师列表的上面,所以我们将下面这些代码复制粘贴到list.vue页面的template标签的div标签下的最前面

<!--条件查询表单-->
<el-form :inline="true" class="demo-form-inline">
  <el-form-item>
    <el-input v-model="searchObj.name" placeholder="讲师名"/>
  </el-form-item>

  <el-form-item>
    <el-select v-model="searchObj.level" clearable placeholder="讲师头衔">
      <el-option :value="1" label="高级讲师"/>
      <el-option :value="2" label="首席讲师"/>
    </el-select>
  </el-form-item>

  <el-form-item label="添加时间">
    <el-date-picker
      v-model="searchObj.begin"
      type="datetime"
      placeholder="选择开始时间"
      value-format="yyyy-MM-dd HH:mm:ss"
      default-time="00:00:00"
    />
  </el-form-item>
  <el-form-item>
    <el-date-picker
      v-model="searchObj.end"
      type="datetime"
      placeholder="选择截止时间"
      value-format="yyyy-MM-dd HH:mm:ss"
      default-time="00:00:00"
    />
  </el-form-item>

  <el-button type="primary" icon="el-icon-search" @click="fetchData()">查询</el-button>
  <el-button type="default" @click="resetData()">清空</el-button>
</el-form>

在这里插入图片描述

  • 我们在data() {...}中定义了条件对象变量teacherQuery,所以我们需要让输入框、下拉框与teacherQuery对象中对应属性进行双向绑定(比如说:v-model="teacherQuery.name"就是和teacherQuery对象的name属性进行双向绑定)。所以我们需要将上面这些代码中的searchObj.改为teacherQuery.
  • 点击"查询"需要调用getList方法,所以需要将截图中第35行的@click="fetchData()"改为@click="getList()"
  • 截图中第36行调用的resetData方法我们会在"7.3添加清空功能"中定义,这里先不管
  • teacherQuery对象的属性名name、level、begin、end可不是随便起的:因为前端是把teacherQuery对象(js对象)转换为json数据传给后端,后端接收到数据后又将json数据封装到TeacherQuery类型的对象(java对象)里面,所以这里teacherQuery对象的属性名必须和我们创建的TeacherQuery类的属性一一对应,而TeacherQuery类的属性就是name、level、begin、end,所以这里teacherQuery对象的属性名就必须是name、level、begin、end

在这里插入图片描述

  • 表单中选择时间的代码也可以从element-ui的示例中找,然后拿过来根据需求修改

在这里插入图片描述

2.刷新页面,在"讲师名"输入框输入"李",查看效果

在这里插入图片描述

7.3添加清空功能

1.我们想要点击"清空"按钮后可以做两件事:清空表单中输入的条件查询所有数据

我们看"7.2使用老师给的代码"的第一个截图的第36行@click="resetData()",所以我们在methods: {...}中编写方法resetData

//"清空"的方法
resetData() {
    
    
//1.表单输入项数据清空
this.teacherQuery = {
    
    }

//2.查询所有讲师数据
this.getList()
}

在这里插入图片描述

this.teacherQuery = {}是让teacherQuery对象为空,这样的话该对象没有属性了,那么表单中的数据自然也就清空了

2.刷新页面,自行进行测试

8.讲师删除

8.1添加删除按钮并给按钮绑定事件

我们在"5.讲师列表"的"5.5显示讲师列表"的第4步的④这一步中复制了老师给的代码到list.vue中,老师给的代码中本来就有删除按钮的代码,并已经给按钮绑定事件了,下面是list.vue页面的部分截图

在这里插入图片描述

8.2在api中定义删除方法

在src–>api–>edu–>teacher.js文件中定义方法,方法内部调用后端的逻辑删除接口

//2.删除讲师
deleteTeacherId(id) {
    
    
  return request({
    
    
    url: `/eduservice/edu-teacher/${
      
      id}`,
    method: 'delete'
  })
}

在这里插入图片描述

因为后端的逻辑删除方法上的注解是@DeleteMapping,所以这里提交方式要用delete

8.3在list.vue中定义删除方法

在list.vue的methods: {...}中定义removeDataById方法,方法内部调用"8.2在api中定义删除方法"中定义的deleteTeacherId方法

1.为了用户体验,当用户点击"删除"按钮时需要弹出一个提示框,和以前一样,我们去element-ui中找代码。代码是从组件中的"MessageBox 弹框"的"确认消息"找到的,我们将红方框圈起来的部分复制到vue.js中然后根据需求修改

在这里插入图片描述

2.removeDataById方法如下

//删除讲师
removeDataById(id) {
    
    
  this.$confirm('此操作将永久删除讲师记录, 是否继续?', '提示', {
    
    
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    
     //点击确定会执行then方法
    //调用teacher.js中定义的删除方法
    teacher.deleteTeacherId(id)
      .then(response => {
    
     //删除成功
        //1.提示删除成功
        this.$message({
    
    
          type: 'success',
          message: '删除成功!'
        });
        //2.回到列表页面
        this.getList()
      })
      .catch(error => {
    
    }) //删除失败
  })
}

在这里插入图片描述

绿框圈起来的部分就是第1步中红框圈起来的代码,我们根据需求进行了修改

8.4测试

刷新页面,自行测试

9.讲师添加

9.1初始化页面

1.先去src–>router–>index.js中看,从component: () => import('@/views/edu/teacher/save')我们知道了点击"添加讲师"后会去views–>edu–>teacher–>save.vue,所以我们在save.vue中编写代码

在这里插入图片描述

2.具体怎么编写代码呢,我们知道当点击首页的"添加讲师"路由时进入表单页面供我们填写信息,所以说我们需要给save.vue页面添加表单组件,老办法,去element-ui中复制过来再根据需求修改,老师已经给了我们修改后的代码,如下。save.vue中是有代码的,是在"5.2创建路由对应的页面"的第4步编写的,我们将save.vue中的代码都删掉,然后将下面的代码复制到save.vue中

<template>
  <div class="app-container">
    <el-form label-width="120px">
      <el-form-item label="讲师名称">
        <el-input v-model="teacher.name"/>
      </el-form-item>
      <el-form-item label="讲师排序">
        <el-input-number v-model="teacher.sort" controls-position="right" min="0"/>
      </el-form-item>
      <el-form-item label="讲师头衔">
        <el-select v-model="teacher.level" clearable placeholder="请选择">
          <!--
            数据类型一定要和取出的json中的一致,否则没法回填
            因此,这里value使用动态绑定的值,保证其数据类型是number
          -->
          <el-option :value="1" label="高级讲师"/>
          <el-option :value="2" label="首席讲师"/>
        </el-select>
      </el-form-item>
      <el-form-item label="讲师资历">
        <el-input v-model="teacher.career"/>
      </el-form-item>
      <el-form-item label="讲师简介">
        <el-input v-model="teacher.intro" :rows="10" type="textarea"/>
      </el-form-item>

      <!-- 讲师头像:TODO -->

      <el-form-item>
        <el-button :disabled="saveBtnDisabled" type="primary" @click="saveOrUpdate">保存</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      teacher: {
        name: '',
        sort: 0,
        level: 1,
        career: '',
        intro: '',
        avatar: ''
      },
      saveBtnDisabled: false //点击一次"保存"按钮后禁用该按钮
    }
  },
  created() {

  },
  methods: {

  }
}
</script>

在这里插入图片描述

  • 截图中的40-47行:我们在"7.1去element-ui找示例代码"的第2步的截图中说过:因为进行了双向数据绑定,所以这里定义或不定义对象的属性都是可以的,我们在"7.1去element-ui找示例代码"没有给对象定义属性,这里换一种写法:给对象定义属性

  • 截图中第30行的:disabled="saveBtnDisabled"和第48行的saveBtnDisabled: false共同作用,目的是:当点击"保存"按钮成功添加讲师后,不能再点击该按钮,防止重复多次提交造成添加多条数据

3.刷新页面,点击路由"添加讲师"看效果

在这里插入图片描述

9.2在api中定义添加方法

在src–>api–>edu–>teacher.js文件中定义方法,方法内部调用后端的添加讲师接口

//3.添加讲师
addTeacher(teacher) {
    
    
  return request({
    
    
    url: `/eduservice/edu-teacher/addTeacher`,
    method: 'post',
    data: teacher
  })
}

在这里插入图片描述

data: teacher在"5.3在api目录下创建js文件,定义方法"的第4步说过,是为了将teacher对象转换为json数据传递到接口中

9.3在save.vue中定义添加方法

1.先在<script>标签中的最前面添加如下代码,以引入teacher.js文件

import teacherApi from '@/api/edu/teacher'

在这里插入图片描述

2.在save.vue的methods: {...}中定义添加方法,该方法内部调用"9.2在api中定义添加方法"中定义的addTeacher方法

methods: {...}中完整代码如下:

methods: {
    
    
  saveOrUpdate() {
    
    
    //调用methods: {...}中定义的添加讲师的方法saveTeacher
    this.saveTeacher()
  },
  //添加讲师的方法
  saveTeacher() {
    
    
    //调用api中添加讲师的方法
    teacherApi.addTeacher(this.teacher)
      .then(response => {
    
    
        //1.提示添加成功
        this.$message({
    
    
          type: 'success',
          message: '添加成功!'
        });
        //2.回到列表页面(路由跳转)
        this.$router.push({
    
    path: '/teacher/table'})
      })
  }
}

在这里插入图片描述

  • 看"9.1初始化页面"的第二张截图的第30行:@click="saveOrUpdate"可以知道:我们给save.vue页面的"保存"按钮绑定的事件是saveOrUpdate,这是因为我们打算让添加讲师和后面说到的修改讲师用同一个表单页面:点击"保存"按钮后会调用saveOrUpdate方法,在该方法内部判断是添加讲师还是修改讲师,然后再调用相应的方法

  • this.$router.push({path: '/teacher/table'}):点击"保存"按钮后此时是在"添加讲师"这个路由中,我们要切换到"讲师列表"路由,这在专业术语上叫路由跳转(其实就相当于后端的重定向)。这行代码的作用:相当于我们手动点击了"讲师列表"路由,这样就回到了讲师列表页面

9.4优化后端代码

1.添加讲师后回到列表页面,此时列表页面是没有做排序的,从用户角度来看,肯定希望列表页面的第一条是刚刚添加的新数据,所以我们需要用创建时间降序排序,这样的话,讲师数据添加的越晚,排序就越靠前

2.去service_edu模块的控制层的EduTeacherController类的pageTeacherCondition方法(分页查询讲师)中添加如下两行代码

//使用创建时间做降序排序
wrapper.orderByDesc("gmt_create");

在这里插入图片描述

9.5测试

重启后端项目,刷新页面点击"添加讲师"路由后填写信息进行测试

在这里插入图片描述

在这里插入图片描述

10.讲师修改

10.1添加修改按钮

我们在"5.5显示讲师列表"的第4步的④这一步中复制了老师给的代码到list.vue中,老师给的代码中本来就有修改按钮的代码,下面是list.vue页面的部分截图

在这里插入图片描述

10.2路由跳转

1.从"10.1添加修改按钮"截图中可以看到,使用了router-link标签,所以这个"修改"按钮是通过路由跳转进入表单页面。我们需要在src–>router–>index.js中添加一个"修改讲师"的路由,但是,这个路由不需要在"讲师管理"路由下显示("讲师列表"路由和"添加讲师"路由都是在"讲师管理"路由下显示的),因为"修改讲师"路由已经在讲师列表页面了(就是讲师列表页面的"修改"按钮)

2.在src–>router–>index.js中添加如下代码:

{
    
    
  path: 'edit/:id',
  name: 'EduTeacherEdit',
  component: () => import('@/views/edu/teacher/save'),
  meta: {
    
     title: '编辑讲师', noCache: true },
  hidden: true
}

在这里插入图片描述

  • path: 'edit/:id'中的:id就相当于以占位符,我们保存index.js的修改后在地址栏输入http://localhost:9528/#/teacher/edit/10086就会跳转到表单页面(10086是随便输入的,你把10086换成中国,效果也是一样)

在这里插入图片描述

  • 我们在"9.3在save.vue中定义添加方法"的第2步说过,添加和修改使用同一个表单页面,所以这里和"添加讲师"路由一样,都是component: () => import('@/views/edu/teacher/save')
  • hidden: true的作用就是隐藏路由

3.我们刚刚设置了"修改讲师"路由的地址是edit/:id,那自然要去list.vue中将"修改"按钮的访问路径修改一下:

在这里插入图片描述

4.刷新页面,在"讲师列表"页面随便找一条数据然后点击"修改"按钮,发现可以跳转到表单页面,并且看地址的最后,1559028065118490625,这就是该条讲师数据的id

在这里插入图片描述

10.3数据回显

点击"修改"按钮后进入表单页面,我们需要根据id从数据库中查询数据,并将查询到的数据填充到表单中,这就叫数据回显

10.3.1在api中定义查询方法

在src–>api–>edu–>teacher.js文件中定义方法,方法内部调用后端的根据id查询讲师接口

//4.根据id查询讲师
getTeacherInfo(id) {
    
    
  return request({
    
    
    url: `/eduservice/edu-teacher/getTeacher/${
      
      id}`,
    method: 'get'
  })
}

在这里插入图片描述

10.3.2在save.vue中定义查询方法

在save.vue的methods: {...}中定义"根据讲师id查询数据 "的方法,该方法内部调用在"10.3.1在api中定义查询方法"中定义的getTeacherInfo方法

//根据讲师id查询数据
getInfo(id) {
    
    
  teacherApi.getTeacherInfo(id)
    .then(response => {
    
    
        this.teacher = response.data.teacher
    })
},

在这里插入图片描述

  • response.data.teacher中的.teacher是因为我们后端getTeacher方法返回的数据中键名是teacher

在这里插入图片描述

10.3.3调用方法"根据id查询数据"

1.添加、修改讲师时使用的都是save.vue页面,但我们只想在执行修改操作时数据回显,执行添加操作时不进行数据回显,所以就需要判断此时是修改还是添加,判断方法有很多种,我们使用这一种:判断路径中是否有id值,有id值的就是修改操作,进行数据回显;没有id值的就是添加操作,不进行数据回显

2.我们在created() {...}中判断路径是否有id值,如果有id值,就调用在"10.3.2在save.vue中定义查询方法"中定义的方法getInfo

created() {
    
    
  //路径中有id值,做修改
  if(this.$route.params && this.$route.params.id) {
    
    
    //从路径获取id值
    const id = this.$route.params.id
    //调用method: {...}中定义的方法"根据讲师id查询数据"
    this.getInfo(id)
  }
},

在这里插入图片描述

  • this.$route.params表示得到路由中的参数
  • this.$route.params.id表示得到路由中名字为id的参数
  • 这里的id和src–>router–>index.js中path: 'edit/:id'的id对应。如果修改为path: 'edit/:iiiddd',那么这里的this.$route.params.id就要修改为this.$route.params.iiiddd

在这里插入图片描述

10.3.4测试

刷新页面,在"讲师列表"页面随便找一条数据然后点击"修改"按钮,发现可以跳转到表单页面,并且实现了数据回显

在这里插入图片描述

10.4在api中定义修改方法

在src–>api–>edu–>teacher.js文件中定义方法,方法内部调用后端的"修改"接口

//5.修改讲师
updateTeacherInfo(teacher) {
    
    
  return request({
    
    
    url: `/eduservice/edu-teacher/updateTeacher`,
    method: 'post',
    data: teacher
  })
}

在这里插入图片描述

10.5在save.vue中定义修改方法

在save.vue的methods: {...}中定义修改方法,该方法内部调用"10.4在api中定义修改方法"中定义的updateTeacherInfo方法

//修改讲师的方法
updateTeacher() {
    
    
  teacherApi.updateTeacherInfo(this.teacher)
    .then(response => {
    
    
      //1.提示修改成功
      this.$message({
    
    
        type: 'success',
        message: '修改成功!'
      });
      //2.回到列表页面(路由跳转)
      this.$router.push({
    
    path: '/teacher/table'})
    })
},

在这里插入图片描述

10.6完善saveOrUpdate方法

我们在"9.3在save.vue中定义添加方法"的第2步说过,点击save.vue页面的"保存"按钮后会执行saveOrUpdate方法,在该方法内部判断是修改操作还是添加操作,然后调用相应的方法。

所以我们需要完善saveOrUpdate方法,完整的saveOrUpdate方法如下:

saveOrUpdate() {
    
    
  //判断是修改还是添加
  //通过判断teacher对象中是否有id来实现
  if(!this.teacher.id) {
    
    
    //添加
    //调用methods: {...}中定义的添加讲师的方法saveTeacher
    this.saveTeacher()
  } else {
    
    
    //修改
    this.updateTeacher()
  }
},

在这里插入图片描述

修改操作时的"10.3.2在save.vue中定义查询方法"中的代码this.teacher = response.data.teacher将查询到的讲师对象赋值给save.vue页面中定义的teacher对象,所以teacher对象中一定有id值;而添加操作时id是在后端自动生成的,所以teacher对象中一定没有id值。所以我们根据teacher对象中是否有id来进行判断

10.7测试

刷新页面,自行进行测试

10.8路由切换问题

10.8.1遇到的问题

1.在讲师列表页随便选择一条数据,点击"修改"按钮

在这里插入图片描述

2.跳转到表单页面,并且数据已经自动回显

在这里插入图片描述

3.此时再点击"添加讲师"路由

在这里插入图片描述

4.此时仍是表单页面,但是此时页面的数据也回显了,显然这并不是我们想要的(我们的需求是点击"添加讲师"路由后表单页面不发生数据回显,而是让我们自己填写信息)

在这里插入图片描述

5.接着往下操作,还有bug:当我们点击"保存"按钮后它提示"修改成功"而不是"添加成功",然后去数据库查看数据,发现并没有插入一条新的数据,而是对原来的数据(讲师名称是:张三三三三三)进行了更新。也就是说,此时点击"保存按钮"项目当成了修改讲师而不是添加讲师

在这里插入图片描述

在这里插入图片描述

10.8.2解决方法(未解决)

1.先点击"修改"按钮,再点击"添加讲师"路由,此时表单页面数据回显的原因是:此时表单页面的teacher对象有值,所以解决方法是:点击"添加讲师"路由后,让表单页面的teacher对象为空,这样就可以清空表单数据了

2.在save.vue的created() {...}中添加如下代码:

else {
    
     //路径中没有id值,做添加
    //清空表单
    this.teacher = {
    
    }
}

在这里插入图片描述

3.保存代码,刷新页面,再次执行"10.8.1遇到的问题"中的步骤,发现执行到第4步,此时点击"添加讲师"路由后表单页面仍回显,说明问题没有得到解决

4.原因:点击"修改"路由后跳转到表单页面,再点击"添加讲师"路由后也是跳转到表单页面,如果两个路由两次跳转到的页面相同,那么只会执行一次"created() {…}"方法,所以刚刚测试时点击"添加讲师"路由后并不会再执行一次created() {...}方法,那么我们在第2步添加的代码也就不可能被执行,所以没有办法清空表单,最后结果是仍然会有数据回显

10.8.3解决方法(已解决)

1.在methods: {...}中定义方法init() {...},将created() {...}方法中的代码抽取出来放到init方法中,然后在created方法中调用init方法

在这里插入图片描述

2.添加如下代码

//监听
watch: {
    
    
  $route(to, from){
    
     //路由变化方式,只要路由发生变化,该方法就会执行
    this.init()
  }
},

在这里插入图片描述

$route(to, from){...}是固定写法,表示路由变化方式,只要路由发生变化,该方法就会执行。这样的话,本来点击"修改"路由时在表单页面,再点击"添加讲师"路由,此时仍在表单页面,但是因为路由发生了变化,所以就会执行$route(to, from){...}方法。

  • 在"讲师列表"页点击"修改"路由,此时执行的是created中的this.init()
  • 在"讲师列表"页点击"修改"路由,然后点击"添加讲师"路由,此时执行的是$route(to, from){...}中的this.init()
  • 页面此时在"讲师列表"页,我们直接点击"添加讲师"路由,此时执行的是created中的this.init()

3.保存代码,刷新页面,再次执行"10.8.1遇到的问题"中的步骤,将所有步骤执行完也不会再出bug了

猜你喜欢

转载自blog.csdn.net/maxiangyu_/article/details/127020114