springboot项目:瑞吉外卖 前后端详细分析 part 1

Part1 第一部分(本页)

Part 第2部分链接

1 后台用户登入、退出、请求拦截

1.1用户登入

1.1.1 开始前逻辑梳理

  • 前端点击登入后,发送请求到了后端,注意这个路径的命名规范:从视图解析器向下走,到对应的资源路径,改为资源.do(优点就是在拦截器配置拦截路径的时候很方便)
  • controller都是对前端请做出的一个响应,返回的东西基本上都封装成对象返回回去(使用RetObj类和前端进行交换,信息交换,例如添加员工失败,就可以返回这个类的对象,将提示信息传到前台去弹窗显示),这个对象放到公共的包里去。
  • 前端的数据直接封装成对象给controller,注意前端提交的名字和后端实体类的属性名称要一致!注意添加(@RequestBody)给参数Employee employee
  • 然后 一般会想获取登入成功员工的信息,存到session中,注意方法

在这里插入图片描述

  • 通过点击登入按钮后 F12 中url,method(post),数据形式(json)就可以编写后端controller

1.1.2 前端代码理解

  • vue 主要代码
methods: { //方法:
        async handleLogin() { //点击登入按钮的时候就会调用(去找前面的点击,对应的handleLogin()),async异步操作,关键词用在函数上
          this.$refs.loginForm.validate(async (valid) => {//进行校验,主要校验表单是不是为空
            if (valid) {//如果校验通过
              this.loading = true //(显示登入中...)前面有span v-if标签对应着
              let res = await loginApi(this.loginForm) //验证通过后,通过ajax发送请求,请求的路径点击进去修改!res是controller响应回来的结果;await关键子用于async函数当中(await可以得到异步的结果)
              //loginForm 里面放的就是用户名密码。
              if (String(res.code) === '1') {
                比如说员工emp的实体,放在这个属性上,前端页面能转为json,保存到浏览器中
                localStorage.setItem('userInfo',JSON.stringify(res.data))
                window.location.href= '/backend/index.html'
              } else {
                //弹窗res的msg属性,之前这里一直报错F12:Uncaught (in promise) TypeError: Cannot set properties of undefined (setting 'type')
                //原因是你自己设置的属性是message不是msg,这里把属性统一改成了msg
                /*
                  vue语法:提示窗口,有点像alert
                  this.$message({
                      message:'保存成功了,我是message',
                      type: 'success'
                  })
                 */
                this.$message.error(res.msg)
                this.loading = false
              }
            }
          })
        }
  • 注意loginApi()和logoutApi()是单独在一个脚本文件中,主要功能就是进行ajax处理!
function loginApi(data) {
  return $axios({
    'url': '/employee/backend/page/login/login.do',
    'method': 'post',
    data
  })
}

function logoutApi(){
  return $axios({
    'url': '/employee/backend/page/login/logout.do',
    'method': 'post',
  })
}

1.1.3 后端代码

  • 前端用json字符串格式向后台传请求参数,那么后台需要采用==@RequestBody==来处理请求的json格式数据,将json数据转换为java对象,否则springmvc就不能解析导致传空参的结果
  • 如果加上@ResponseBody注解,就不会走视图解析器,不会返回页面,目前返回的json数据。如果不加,就走视图解析器,返回页面
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;
    /**
     * 员工登录
     * @param request
     * @param employee
     * @return
     */
    @PostMapping("/login")
    public R<Employee> login(HttpServletRequest request,@RequestBody Employee employee){
        //是用json字符串格式向后台传请求参数,那么后台需要采用@RequestBody来处理请求的json格式数据,将json数据转换为java对象,否则springmvc就不能解析导致传空参的结果
        //如果加上@ResponseBody注解,就不会走视图解析器,不会返回页面,目前返回的json数据。如果不加,就走视图解析器,返回页面

        //1、将页面提交的密码password进行md5加密处理
        String password = employee.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());

        //2、根据页面提交的用户名username查询数据库
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Employee::getUsername,employee.getUsername());
        Employee emp = employeeService.getOne(queryWrapper);

        //3、如果没有查询到则返回登录失败结果
        if(emp == null){
            return R.error("登录失败");
        }

        //4、密码比对,如果不一致则返回登录失败结果
        if(!emp.getPassword().equals(password)){
            return R.error("登录失败");
        }

        //5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
        if(emp.getStatus() == 0){
            return R.error("账号已禁用");
        }

        //6、登录成功,将员工id存入Session并返回登录成功结果
        request.getSession().setAttribute("employee",emp.getId());
        return R.success(emp);
    }
}

1.2 用户退出

  • 找到前端对应的样式的位置
    在这里插入图片描述

  • 找到对应vue代码部分
    在这里插入图片描述
    分析:这里removeItem是删除前端存储的内容;前端显示管理员是xxx,利用了local storage

    const userInfo = window.localStorage.getItem('userInfo')
    if (userInfo) {
      this.userInfo = JSON.parse(userInfo)
    }
    

按F12去看,注意,userInfo是数据模型中添加的,登入成功后就storyge了,之后在index.html中又有进行调用,显示出管理员是谁(退出按钮的旁边)
在这里插入图片描述

  • 找到logoutApi中对应的代码
function loginApi(data) {
  return $axios({
    'url': '/employee/backend/page/login/login.do',
    'method': 'post',
    data
  })
}

function logoutApi(){
  return $axios({
    'url': '/employee/backend/page/login/logout.do',
    'method': 'post',
  })
}

  • 修改完上面的url,找到methode之后,现在可以写后端的controller了
    (也可以用F12看,因为点击按钮后会发送一个请求,就可以看到这个请求的method,url…)
@PostMapping("/employee/backend/page/login/logout.do")
public RetObj<String> logout(HttpServletRequest request){
    //清理Session中保存的当前登录员工的id
    request.getSession().removeAttribute(Contants.SESSION_USERID);
    //登入成功了,把对应的对象传给前端;这里的obj就是一个String,前端会显示一个弹窗:退出成功!
    return RetObj.success("退出成功!");//retObj.data = obj;
    }
  • 在哪实现的跳转到登入页面?

RetObj.success("退出成功!") controller返回这个对象之后,code=1,在下面进行了验证,验证完毕后删除浏览器的数据userInfo,并完成跳转!
注:一个找了很久的bug:res.code === 1,还有很多其他地方,前端是写成数字进行校验的,而你的RetObj类中的属性的字符串的“0”,“1”,所以一直无法通过,细节注意起来。

 methods: {
   logout() {
     logoutApi().then((res)=>{
       if(res.code === 1){
         localStorage.removeItem('userInfo')
         window.location.href = '/backend/page/login/login.html'
       }
     })
   },

1.3 拦截请求

在这里插入图片描述
在这里插入图片描述
只拦截数据的请求,对于页面和静态资源,想看就看,应该放行。

  • 代码实现:
    先配置
package cn.edu.uestc.ruijitakeout.common.config;

import cn.edu.uestc.ruijitakeout.backend.interceptor.LoginInterceptor;
import cn.edu.uestc.ruijitakeout.common.JacksonObjectMapper.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Slf4j
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
	//消息转换器
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("拓展消息转换器成功加载");
        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用Jackson将Java对象转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器对象追加到mvc框架的转换器集合中
        converters.add(0,messageConverter);
    }
	//拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //重写方法,添加拦截器方法
        registry.addInterceptor(loginInterceptor())
                //拦截哪些路径
                .addPathPatterns("/**")
                //不拦截路径
                .excludePathPatterns("/employee/backend/page/login/login.do",
                        "/backend/**",
                        "/employee/backend/page/login/logout.do",
                        "/front/**",
                        "/error"
                );
    }

    @Bean
    public LoginInterceptor loginInterceptor(){
        return new LoginInterceptor();
    }

}

拦截器:

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();
        log.info("当前路径:{}", uri);

        /**
         * HandlerMethod=>Controller中标注@RequestMapping的方法
         *  需要配置静态资源不拦截时,添加这块逻辑  => 前后端分离项目
         *
         */
        // 是我们的conrtoller中的方法,如果不是的话,放行,给加载静态资源
/*        if (!(handler instanceof HandlerMethod)) {
            log.info("是静态资源或非controller中的方法,放行");
            return true;
        }*/
        //通过session判断是否登入
        if (request.getSession().getAttribute(Contants.SESSION_USERID) == null) {
            //这里应该跳转到登入页面,,如何做?
            //5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
            log.info("用户未登入,通过输出流方式向客户端页面响应数据,打回登入页面");
            response.getWriter().write(JSON.toJSONString(RetObj.error("NOTLOGIN")));//与前端request.js中的代码呼应
            return false;
        } else {
            log.info("用户已经登入,id={}", request.getSession().getAttribute(Contants.SESSION_USERID));
            return true;
        }
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

2 员工管理

2.1 index.html页面前端分析

  • 登入完成后,进入backend/index.html之后,对该页面进行开发
    在这里插入图片描述
  • 注意,index.html仅仅只是左边的一小个页面(上图黑色的部分),点击左边的菜单导航栏,对应跳转到url。前端并且设置了默认url,一进入就可以直接跳转到对应的页面,可以修改成baidu,会在右侧显示出百度的官网,右侧的各个页面在另外的目录下:/page/…在这里插入图片描述
  • 点击index.html中的菜单,转到对应的html,注意menuHandle()方法
    在这里插入图片描述
menuHandle(item, goBackFlag) {
            this.loading = true
            this.menuActived = item.id
            //这里的url就是前面数据部分里写的url,进行了跳转;
            //iframeUrl在前面定义了,会跳到除了导航栏右边的页面,有iframe定义
            this.iframeUrl = item.url
            this.headTitle = item.name
            this.goBackFlag = goBackFlag
            this.closeLoading()
          },

2.2 添加员工

2.2.1 开始前思路整理

  • 下面这个页面是点击“添加员工”按钮之后显示出来的,自己去看前端的逻辑,点击事件,跳转。
    前端设置了一些不能未空的校验,以下就是需要输入的字段。
    在这里插入图片描述
  • 注意:员工的名字唯一,如果新建添加员工前端页面重复添加同样名字的表单,抛出异常的最底层是从数据库开始抛出的,这个问题要在下面得到解决。
  • 基本流程
    可以打开F12调试,输入对应的信息,查看url,method(post),提交数据的方式(json),这些东西就可以用来写后端的controller了!而不需要花很多时间研究前端的提交流程(也该理解)
    在这里插入图片描述

2.2.2 前端跳转逻辑

  • 新增员工按钮自己去看,也是一个点击事件发生跳转。下面这个是输入好员工信息后发生的点击事件跳转,现在去看对应的方法:submitForm()
    在这里插入图片描述
  • submitForm() 先进行校验…注意addEmployee(params).then(res => {.. 这个请求ajax方法的理解! 下面代码中主要对响应回来的信息进行处理。
submitForm (formName, st) {
  this.$refs[formName].validate((valid) => {
    if (valid) {
      if (this.actionType === 'add') {
        const params = { //把表单数据拿过来封装成json对象,在下面的函数中传给后端
          ...this.ruleForm,
          sex: this.ruleForm.sex === '女' ? '0' : '1'
        }
        //这个方法和前面的登入、退出类似,封装到了一个js文件中,调用ajax方法,url...与F12对应上了
        //页面返回信息后执行回调函数then()里面的逻辑
        addEmployee(params).then(res => {
          if (res.code === 1) {
            this.$message.success('员工添加成功!')
            if (!st) {
              this.goBack()
            } else {
              this.ruleForm = {
                username: '',
                'name': '',
                'phone': '',
                // 'password': '',
                // 'rePassword': '',/
                'sex': '男',
                'idNumber': ''
              }
            }
          } else {
            this.$message.error(res.msg || '操作失败')
          }
        }).catch(err => {
          this.$message.error('请求出错了:' + err)
        })
  • addEmployee,点进去对应ajax请求
// 新增---添加员工
function addEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'post',
    data: { ...params }
  })
}

2.2.3 后端代码编写

package cn.edu.uestc.ruijitakeout.backend.controller.workbench;
@Slf4j
@RestController
public class CRUDEmployeeController {

    @Resource
    EmployeeService employeeService;

    @PostMapping("/employee/backend/page/member/add.do")
    public RetObj addEmp(@RequestBody Employee employee, HttpServletRequest request){
        log.info("成功进入到addEmp的controller");
        //需要为employee设置一些前端没提交的字段数据create_time、update_time、create_user、update_user
        //学会使用LocalDateTime
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        //设置初始化密码
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        //create_user,从session中拿
        employee.setCreateUser((Long) request.getSession().getAttribute(Contants.SESSION_USERID));
        employee.setUpdateUser((Long) request.getSession().getAttribute(Contants.SESSION_USERID));

        boolean save = employeeService.save(employee);
        return RetObj.success("成功添加员工!");
    }
}

2.2.4 解决用户名等字段重复,导致数据库添加错误问题

  • 全局异常处理中@ControllerAdvice(…)底层是代理,代理controller,将controller中的方法作为切面切到手。
  • 使用到了Spring切面,将某些controller,出现某些异常的时候,作为切面切出来处理
  • 主要思路还是使用RetObj返回类中设置信息!

在这里插入图片描述

  • 报错如下,思路是写一个类(全局捕获比较好,减少代码重复),专门处理异常。像这个重复提交的异常,我们通过RetObj,把重复出来的字段数据截取出来,传给前端做提示。如果是其他类错误:提示信息就写:未知错误!
### Cause: java.sql.SQLIntegrityConstraintViolationException: 
Duplicate entry 'admin2' for key 'employee.idx_username'; 
Duplicate entry 'admin2' for key 'employee.idx_username'; 
nested exception is java.sql.SQLIntegrityConstraintViolationException: 
Duplicate entry 'admin2' for key 'employee.idx_username'] with root cause
  • 全局异常处理代码
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody //因为一会儿的写的方法,需要返回一个json数据,所以需要写这个注解(否则返回的就是那个对象!!)
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public RetObj<String> exceptionHandler(SQLIntegrityConstraintViolationException exception){
        log.error(exception.getMessage());
        if (exception.getMessage().contains("Duplicate entry")){
            String[] split = exception.getMessage().split(" ");
            return RetObj.error(split[2] + "已存在!");
        }else {
            return RetObj.error("未知错误!");
        }
    }    
}

2.2.4 总结

  • 主要理解数据流转
    在这里插入图片描述

2.3 员工信息分页查询

2.3.1 实现思路分析

在这里插入图片描述

第四点注意,Controller或者其他页面到底是如何实现 封装为json的形式再传送前端的?毕竟返回的是returnRetObj;
个人认为就是加了注解:@ResponseBody,在controller中复合注解中包含了
@RestController,所以在写其他类,如果涉及到返回数据给前端,并以json的形式,注意加上@ReponseBody

  • 类似的思路,先去查看F12,页面提交的url和method,以及前端提交的参数:page,pageSize,这样就能够写后端代码了
    在这里插入图片描述

2.3.2 前端代码分析

  • 页面一打开,会自动提交查询请求,主要是通过vue的钩子函数created()(生命周期函数)实现
    在这里插入图片描述
  • 前端可能提交的三个数据:page(页面,默认传过来是1),pageSize(每页条数,默认传过来是10),name(再根据名字模糊查询,这个也是前端一个输入框那边输入的,用于查询的附加条件,旁边有个小按钮(放大镜),点击就执行查询)
  • 模糊查询相关前端逻辑如下,注意clearable属性,点击之后执行handleQuery,清除默认提示。vue的监听组件:@keyup.enter.native
<el-input v-model="input" placeholder="请输入员工姓名" style="width: 250px" 
clearable @keyup.enter.native="handleQuery">
  <i
    slot="prefix"
    class="el-input__icon el-icon-search"
    style="cursor: pointer"
    @click="handleQuery"
  ></i>
</el-input>

handlQquery()

  handleQuery() {
    this.page = 1;//默认值
    this.init();
  },

init()
在这里插入图片描述
包括点击分页条下面的数字,对应跳到第几页也是类似的结构

 handleQuery() {
   this.page = 1;//默认值
   this.init();
 },
  • 分页查询,注意思考前端需要哪些数据,比如Page对象中的recode等数据,那后面写后端代码的时候返回值RetObj<Page> 就要注意传递的Page类型数据了。具体的说:Page pageInfo = new Page(page,pageSize); 后端在执行完条件查询:empService.page(pageInfo,lambdaQueryWrapper) 之后,直接将查询到的结果封装到pageInfo对象中了。后端解析这个对象转成的json,就能够拿到里面的recods等属性值!recodes就是查询到的每个员工的信息,前端代码赋值给了tableData。counts是下面“共xxx条数据显示的”

  • Page类

public class Page<T> implements IPage<T> {
    private static final long serialVersionUID = 8545996863226528798L;
    protected List<T> records;
    protected long total;
    protected long size;
    protected long current;
    protected List<OrderItem> orders;
    protected boolean optimizeCountSql;
    protected boolean searchCount;
    protected boolean optimizeJoinOfCountSql;
    protected String countId;
    protected Long maxLimit;
    ...

在这里插入图片描述

  • 进一步查看或修改getMemberList对应的url、method
function getMemberList (params) {
  return $axios({
    url: '/employee/backend/page/member/add.do/page',
    method: 'get',
    params
  })
}
  • 至于如何展示页面的,主要使用到了vue中的element-ui组件,定了包括每页默认条数等信息。还有一些细节比如后端返回的json数据本来对于账户状态只有1和0,但是在页面上显示了正常、禁用,这就是因为前端进行了处理。
<el-pagination
        class="pageList"
        :page-sizes="[10, 20, 30, 40]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="counts"
        :current-page.sync="page"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      ></el-pagination>

在这里插入图片描述

2.3.3 后端使用MybatisPlus的分页插件进行分页

  • 前端可能提交的三个数据:page(页面,默认传过来是1,前端可以选择查看第几页,这个消息也要传给后端),pageSize(每页条数,默认传过来是10),name(再根据名字模糊查询,这个也是前端一个输入框那边输入的,用于查询的附加条件)。着意味着后端需要写分页构造器和条件构造器。总的来说,对于后端其实很简单,只需要传递给前端一个Page的对象即可,封装为了json对象,records对应就是每个员工的信息,前端会进行循环展示。

  • 配置MP的分页插件

/**
 * MP分页插件的配置
 */
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}
  • 后端controller,插入错误的话应该会进行全局异常捕获。
@GetMapping("/employee/backend/page/member/page.do")
public RetObj<Page<Employee>> PaginationQuery(int page, int pageSize, String name){
    log.info("前端数据:page={},pageSize={},name={}",page,pageSize,name);
    Page<Employee> pageInfo = new Page<>(page,pageSize);
    LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.like(StringUtils.isNotBlank(name),Employee::getName,name)
            .orderByDesc(Employee::getUpdateTime);
    employeeService.page(pageInfo,lambdaQueryWrapper);
    return RetObj.success(pageInfo);
}

2.4 禁用、启用员工账号

2.4.1 实现思路分析

  • 分析需求
    在这里插入图片描述

普通用户的操作栏目:只有编辑功能,没有启用和禁用功能
在这里插入图片描述

  • 开发思路,其实主要操作status就行了,禁用就把相关的id设置为0,所以前端给后端的参数就两个:id status
    在这里插入图片描述
  • 编辑的时候 也可以复用这个update方法!(思路就是controller接收什么消息无所谓,直接使用==@RequestBody封装到Employee对象中去==,这就做到了来什么数据都能封装到里面去,实现了复用),另外 根据 ID 选择修改boolean updateById(T entity);这个如果entity是null的字段就不修改,只修改非null的字段(充分体现了是update)
    在这里插入图片描述
  • 出现的问题:禁用的时候根据id update语句中id多了两个零。去查看当时分页查询的数据(分页查询的结果就是现在操作的行),发现当时提交过来的id也是正常的。问题是出在js的精度上。
    在这里插入图片描述
    在这里插入图片描述
    解决方案:
    在这里插入图片描述

2.4.2 前端代码分析

  • 实现普通用户在操作栏目 只有 “编辑” 功能,而管理员能够 “启用"、“禁用” 其他员工账号。前端通过localstorage拿到当前登入的用户名,之后 v-if="user === 'admin'"判断是否显示按钮{{ scope.row.status == '1' ? '禁用' : '启用' }}
<el-table-column label="账号状态">
  <template slot-scope="scope">
    {{ String(scope.row.status) === '0' ? '已禁用' : '正常' }}
  </template>
</el-table-column>
<el-table-column
  label="操作"
  width="160"
  align="center"
>
  <template slot-scope="scope">
    <el-button
      type="text"
      size="small"
      class="blueBug"
      @click="addMemberHandle(scope.row.id)"
      :class="{notAdmin:user !== 'admin'}"
    >
      编辑
    </el-button>
    <el-button
      type="text"
      size="small"
      class="delBut non"
      @click="statusHandle(scope.row)"
      v-if="user === 'admin'"
    >
      {{ scope.row.status == '1' ? '禁用' : '启用' }}
    </el-button>
  • 点击启用、禁用按钮后的逻辑。如上代码,点击按钮触发函数并传递参数statusHandle(scope.row) (scope.row)就是当条数据对应的json对象。之后enableOrDisableEmployee()方法(该方法封装到了js文件中)发送ajax请求。到这里,服务器肯定要写controller去接收处理这个请求。
 //状态修改
statusHandle (row) {
  this.id = row.id
  this.status = row.status
  //$confirm是elementUI提供的方法,用于弹出窗口的
  this.$confirm('确认调整该账号的状态?', '提示', {
    'confirmButtonText': '确定',
    'cancelButtonText': '取消',
    'type': 'warning'
    }).then(() => { //confirmButtonText 这个对应的值 ‘确定点击后’,就会指向then后面这个回调函数
      //enableOrDisableEmployee 发请求,三目运算符,当前是0,就传1给后端
    enableOrDisableEmployee({ 'id': this.id, 'status': !this.status ? 1 : 0 }).then(res => {
      console.log('enableOrDisableEmployee',res)
      if (String(res.code) === '1') {
        this.$message.success('账号状态更改成功!')
        this.handleQuery()
      }
    }).catch(err => {
      this.$message.error('请求出错了:' + err)
    })
  })
},

2.4.3 后端代码分析

  • 一开是单独写了禁用的后端代码,后来与edit 下一节的内容进行了整合,就是说禁用这里也算是edit,也是把一些字段(status)封装到Employee对象中,之后进行update.
  • 注意的是,update或者updateById() 都是只更新不为null 的字段,比如这里的Employee 对象只有status属性是0或者1,其他均为null,但是更新过去的时候,其他字段不会变的!,只 update xxxx set(非null字段)
@PutMapping("/employee/backend/page/member/forbidden.do")
public RetObj<String> forbiddenUser(@RequestBody Employee employee, HttpServletRequest request){
    log.info("成功进入controller方法,接收到:id={},status={}",employee.getId(),employee.getStatus());

    LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
    //UPDATE employee SET status=?,update_time=?,update_user=? WHERE (id = ?)
    lambdaUpdateWrapper.set(Employee::getStatus,employee.getStatus())
            //.set(Employee::getUpdateTime,LocalDateTime.now())
            //.set(Employee::getUpdateUser,request.getSession().getAttribute(Contants.SESSION_USERID))
            .eq(Employee::getId,employee.getId());
    boolean res = employeeService.update(lambdaUpdateWrapper);
    log.info("是否成功:{}",res);
    return RetObj.success("成功禁止使用权限");
}

2.5 编辑员工信息

2.5.1 思路分析

在这里插入图片描述

  • 步骤分析:关键在第五步回显,页面回显当前需要修改用户的信息(提交请求,根据id查出信息,显示到上面),在此基础上修改
    在这里插入图片描述

2.5.2 前端分析

  • 先跳到添加页面,并带着id,进行回显,模式是修改
<template slot-scope="scope">
  <el-button
    type="text"
    size="small"
    class="blueBug"
    @click="addMemberHandle(scope.row.id)" /><!--把id传过去,按照id查询出默认信息进行回显-->
    :class="{notAdmin:user !== 'admin'}"
  >
// 添加
addMemberHandle (st) {
  if (st === 'add'){
    window.parent.menuHandle({
      id: '2',
      url: '/backend/page/member/add.html',
      name: '添加员工'
    },true)
  } else {
    window.parent.menuHandle({
      id: '2',
      url: '/backend/page/member/add.html?id='+st,
      name: '修改员工'
    },true)
  }
},
  • 由于修改也是跳转到新增页面进行操作,当vue对象创建完成之后,自动调用构造函数created()
created() { //钩子函数,vue对象创建后,该函数自动执行
  this.id = requestUrlParam('id')
  //三目运算符,如果id有值,this.id= true,则进入edit页面!
  this.actionType = this.id ? 'edit' : 'add'
  if (this.id) {//如果id有值
    this.init()
  }
},

其中requestUrlParam方法

//获取url地址上面的参数
function requestUrlParam(argname){
  var url = location.href//获取完整的请求url路径
  var arrStr = url.substring(url.indexOf("?")+1).split("&")//解析字符串,动态取出id值,split是防止有多个参数
  for(var i =0;i<arrStr.length;i++)//遍历数组
  {
      var loc = arrStr[i].indexOf(argname+"=")
      if(loc!=-1){
          //id=123456...把数字取出来
          return arrStr[i].replace(argname+"=","").replace("?","")
      }
  }
  return ""如果前面的return没执行,那就返回空,什么也不填充
}
  • 查询数据库,进行回显,回显完毕了之后直接修改表单数据,然后点按钮保存修改数据库
methods: {
  async init () {
    queryEmployeeById(this.id).then(res => {//回调函数
      console.log(res)
      if (String(res.code) === '1') {
        console.log(res.data)
        this.ruleForm = res.data//回显,这里就要注意,res的data属性要赋一个Emp类型的值
        this.ruleForm.sex = res.data.sex === '0' ? '女' : '男' //数据库那边是0,1
        // this.ruleForm.password = ''
      } else {
        this.$message.error(res.msg || '操作失败')
      }
    })
  },
  • 保存并继续添加那个按钮只有在单单纯的添加模式下才有
 v-if="actionType == 'add'"
 type="primary"
 class="continue"
 @click="submitForm('ruleForm', true)"
>
 保存并继续添加
</el-button>

2.5.3 后端代码编写

  • 点击编辑按钮后,后端查询信息进行回显
@GetMapping("/employee/backend/page/member/getEmp.do/{id}")
public RetObj<Employee> queryEmpById(@PathVariable Long id){
    Employee emp = employeeService.getById(id);
    if (emp!=null){
        return RetObj.success(emp);
    }
    return RetObj.error("未查询到该员工对应的信息");
}
  • 回显信息到前端,用户进行修改后,点击保存按钮,这里的保存按钮注意是复用的!!! 在新增用户保存的时候,使用的是PostMapping(url),在编辑用户保存的时候,对应的是PutMapping(url),注意这个url可以一样,可以通过Put还是Post去区分不同的controller方法。当然也可以不一样,因为前端使用actionType 判断是否是add,还是edit,会分别调用不同的方法。
submitForm (formName, st) {
   this.$refs[formName].validate((valid) => {
         if (valid) {
           if (this.actionType === 'add') {
             const params = { //把表单数据拿过来封装成json对象,在下面的函数中传给后端
               ...this.ruleForm,
               sex: this.ruleForm.sex === '女' ? '0' : '1'
             }....
             ....
           else{
           ....
           }

这里采用不用的url进行了区分,注意,既然是修改信息,就要想到不仅仅是修改前端的那些基本个人信息,还需要设置updatetime,updateuser,应该思考全面!

@PutMapping("/employee/backend/page/member/edit.do")
public RetObj<String> updateById(@RequestBody Employee emp, HttpServletRequest request){
    emp.setUpdateTime(LocalDateTime.now());
    Long updaterId = (Long) request.getSession().getAttribute(Contants.SESSION_USERID);
    emp.setUpdateUser(updaterId);
    log.info("修改后的emp:{}",emp.toString());

    boolean b = employeeService.updateById(emp);
    if (b){
        return RetObj.success("成功修改信息!");
    }else {
        return RetObj.error("修改失败!");
    }
}

3 分类管理业务开发(见part2)第二篇文章

http://t.csdn.cn/QO1iA

持续更新中…

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值