【瑞吉外卖】day02-项目开发阶段-员工管理

项目开发阶段-员工管理

后台登录退出功能开发

1、展示登录页面

​ 展示完整login.html页面

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>瑞吉外卖管理端</title>
  <link rel="shortcut icon" href="../../favicon.ico">
  <!-- 引入样式 -->
  <link rel="stylesheet" href="../../plugins/element-ui/index.css" />
  <link rel="stylesheet" href="../../styles/common.css">
  <link rel="stylesheet" href="../../styles/login.css">
  <link rel="stylesheet" href="../../styles/icon/iconfont.css" />
  <style>
    .body{
      min-width: 1366px;
    }
  </style>
</head> 

<body>
  <div class="login" id="login-app">
    <div class="login-box">
      <img src="../../images/login/login-l.png" alt="">
      <div class="login-form">
        <el-form ref="loginForm" :model="loginForm" :rules="loginRules" >
          <div class="login-form-title">
            <img src="../../images/login/logo.png" style="width:139px;height:42px;" alt="" />
          </div>
          <el-form-item prop="username">
            <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号" maxlength="20"
              prefix-icon="iconfont icon-user" />
          </el-form-item>
          <el-form-item prop="password">
            <el-input v-model="loginForm.password" type="password" placeholder="密码" prefix-icon="iconfont icon-lock" maxlength="20"
              @keyup.enter.native="handleLogin" />
          </el-form-item>
          <el-form-item style="width:100%;">
            <el-button :loading="loading" class="login-btn" size="medium" type="primary" style="width:100%;"
              @click.native.prevent="handleLogin">
              <span v-if="!loading">登录</span>
              <span v-else>登录中...</span>
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>

  <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="../../plugins/vue/vue.js"></script>
  <!-- 引入组件库 -->
  <script src="../../plugins/element-ui/index.js"></script>
  <!-- 引入axios -->
  <script src="../../plugins/axios/axios.min.js"></script>
  <script src="../../js/request.js"></script>
  <script src="../../js/validate.js"></script>
  <script src="../../api/login.js"></script>

  <script>
    new Vue({
      el: '#login-app',
      data() {
        return {
          loginForm:{
            username: 'admin',
            password: '123456'
          },
          loading: false
        }
      },
      computed: {
        loginRules() {
          const validateUsername = (rule, value, callback) => {
            if (value.length < 1 ) {
              callback(new Error('请输入用户名'))
            } else {
              callback()
            }
          }
          const validatePassword = (rule, value, callback) => {
            if (value.length < 6) {
              callback(new Error('密码必须在6位以上'))
            } else {
              callback()
            }
          }
          return {
            'username': [{ 'validator': validateUsername, 'trigger': 'blur' }],
            'password': [{ 'validator': validatePassword, 'trigger': 'blur' }]
          }
        }
      },
      created() {
      },
      methods: {
        async handleLogin() {
          this.$refs.loginForm.validate(async (valid) => {
            if (valid) {
              this.loading = true
              let res = await loginApi(this.loginForm)
              if (String(res.code) === '1') { //1表示登录成功
                localStorage.setItem('userInfo',JSON.stringify(res.data))
                window.location.href= '/backend/index.html'
              } else {
                this.$message.error(res.msg)
                this.loading = false
              }
            }
          })
        }
      }
    })
  </script>
</body>
</html>
2、构建实体类Employee
/**
 * 员工信息表
 */
@Data
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String username;

    private String name;

    private String password;

    private String phone;

    private String sex;

    private String idNumber;    //身份证号

    private Integer status;     //状态码

    private LocalDateTime createTime;

    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)
    private Long createUser;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

}

3、创建三层架构

​ 在目录下创建controller、service、mapper三个文件夹。

4、创建mapper层

​ 在mapper文件夹下创建EmployeeMapper类,继承Basemapper接口。

@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {

}
5、创建service层

​ EmployeeService继承IService

public interface EmployeeService extends IService<Employee> {
}

​ EmployeeServiceImpl

@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper,Employee> implements EmployeeService {
}
6、创建controller实体类层

​ 其中,controller中包含两个功能:登录账号、退出账号

@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;

    /***
     * 员工登录功能
     */
    @PostMapping("/login")
    public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
        //1.将页面提交的密码password进行md5加密
        String password = employee.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));

        //2.根据页面提交的用户名username查询数据库,从数据库中查询employee表中的数据,根据getUsername字段,查询username为指定数据的值
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        //构建查询条件,Employee::getUsername为实体类的字段名,employee.getUsername()为前端传入的用户名
        queryWrapper.eq(Employee::getUsername,employee.getUsername());
        //查询,如果在字段中查找到,则将其赋值给 emp 变量,否则返回null
        Employee emp = employeeService.getOne(queryWrapper);

        /**
        	Employee::getUsername
        	1.实例化一个Employee对象
            Employee employee = new Employee()
            2.调用对象employee的get方法,这里调用的是getUsername
            employee.getUsername();
        **/
        
        //3.如果没有查询到则返回失败结果
        if (emp==null){
            return R.error("登录失败,请检查用户名是否存在!");
        }

        //4.密码比对,不一致则返回登录失败结果
        if (!emp.getPassword().equals(password)){
            return R.error("登录失败,请检查密码是否正确!");
        }

        //5.查看员工状态,1为正常,0为禁用
        if (emp.getStatus()==0){
            return R.error("登陆失败,当前账号状态不可用!");
        }

        //6.登陆成功,将员工id存入session
        request.getSession().setAttribute("employee",emp.getId());
        return R.success(emp);
    }

    /***
     * 员工退出功能
     */
    @PostMapping("/logout")
    public R<String> logout(HttpServletRequest request){
        //清理session中保存的当前登录的员工id
        request.getSession().removeAttribute("employee");
        return R.success("已退出账号!");
    }
}

7、创建通用返回实体类

​ 创建common文件夹,在文件夹中创建实体类R

/**
 * 通用返回结果,服务端响应的数据最终都会封装成此对象
 * @param <T>
 */
@Data
public class R<T> {

    private Integer code; //编码:1成功,0和其它数字为失败

    private String msg; //错误信息

    private T data; //数据

    private Map map = new HashMap(); //动态数据

    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;
    }

    public static <T> R<T> error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }

}

8、展示后台主页面

​ 展示完整index.html页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>瑞吉外卖管理端</title>
    <link rel="shortcut icon" href="favicon.ico">
    <!-- 引入样式 -->
    <link rel="stylesheet" href="plugins/element-ui/index.css" />
    <link rel="stylesheet" href="styles/common.css" />
    <link rel="stylesheet" href="styles/index.css" />
    <link rel="stylesheet" href="styles/icon/iconfont.css" />
    <style>
      .body{
        min-width: 1366px;
      }
      .app-main{
        height: calc(100% - 64px);
      }
      .app-main .divTmp{
        width: 100%;
        height: 100%;
      }
    </style>
  </head>

  <body>
    <div class="app" id="app">
      <div class="app-wrapper openSidebar clearfix">
        <!-- sidebar -->
        <div class="sidebar-container">
          <div class="logo">
            <!-- <img src="images/logo.png" width="122.5" alt="" /> -->
            <img src="images/login/login-logo.png" alt="" style="width: 117px; height: 32px" />
          </div>

          <el-scrollbar wrap-class="scrollbar-wrapper">
            <el-menu
              :default-active="defAct"
              :unique-opened="false"
              :collapse-transition="false"
              background-color="#343744"
              text-color="#bfcbd9"
              active-text-color="#f4f4f5"
            >
              <div v-for="item in menuList" :key="item.id">
                <el-submenu :index="item.id" v-if="item.children && item.children.length>0">
                  <template slot="title">
                    <i class="iconfont" :class="item.icon"></i>
                    <span>{{item.name}}</span>
                  </template>
                  <el-menu-item
                    v-for="sub in item.children"
                    :index="sub.id"
                    :key="sub.id"
                    @click="menuHandle(sub,false)"
                    >
                    <i :class="iconfont" :class="sub.icon"></i>
                    <span slot="title">{{sub.name}}</span>
                    </el-menu-item
                  >
                </el-submenu>
                <el-menu-item v-else :index="item.id" @click="menuHandle(item,false)">
                  <i class="iconfont" :class="item.icon"></i>
                  <span slot="title">{{item.name}}</span>
                </el-menu-item>
              </div>
            </el-menu>
          </el-scrollbar>
        </div>
        <div class="main-container">
          <!-- <navbar /> -->
          <div class="navbar">
            <div class="head-lable">
              <span v-if="goBackFlag" class="goBack" @click="goBack()"
                ><img src="images/icons/btn_back@2x.png" alt="" /> 返回</span
              >
              <span>{{headTitle}}</span>
            </div>
            <div class="right-menu">
              <div class="avatar-wrapper">{{ userInfo.name }}</div>
              <!-- <div class="logout" @click="logout">退出</div> -->
              <img src="images/icons/btn_close@2x.png" class="outLogin" alt="退出" @click="logout" />
            </div>
          </div>
          <div class="app-main" v-loading="loading">
            <div class="divTmp" v-show="loading"></div>
            <iframe
              id="cIframe"
              class="c_iframe"
              name="cIframe"
              :src="iframeUrl"
              width="100%"
              height="auto"
              frameborder="0"
              v-show="!loading"
            ></iframe>
          </div>
        </div>
      </div>
    </div>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="plugins/vue/vue.js"></script>
    <!-- 引入组件库 -->
    <script src="plugins/element-ui/index.js"></script>
    <!-- 引入axios -->
    <script src="plugins/axios/axios.min.js"></script>
    <script src="js/request.js"></script>
    <script src="./api/login.js"></script>
    <script>

      new Vue({
        el: '#app',
        data() {
          return {
            defAct: '2',
            menuActived: '2',
            userInfo: {},
            menuList: [
              // {
              //   id: '1',
              //   name: '门店管理',
              //   children: [
                  {
                    id: '2',
                    name: '员工管理',
                    url: 'page/member/list.html',
                    icon: 'icon-member'
                  },
                  {
                    id: '3',
                    name: '分类管理',
                    url: 'page/category/list.html',
                    icon: 'icon-category'
                  },
                  {
                    id: '4',
                    name: '菜品管理',
                    url: 'page/food/list.html',
                    icon: 'icon-food'
                  },
                  {
                    id: '5',
                    name: '套餐管理',
                    url: 'page/combo/list.html',
                    icon: 'icon-combo'
                  },
                  {
                    id: '6',
                    name: '订单明细',
                    url: 'page/order/list.html',
                    icon: 'icon-order'
                  }
              //   ],
              // },
            ],
            iframeUrl: 'page/member/list.html',
            headTitle: '员工管理',
            goBackFlag: false,
            loading: true,
            timer: null
          }
        },
        computed: {},
        created() {
          const userInfo = window.localStorage.getItem('userInfo')
          if (userInfo) {
            this.userInfo = JSON.parse(userInfo)
          }
          this.closeLoading()
        },
        beforeDestroy() {
          this.timer = null
          clearTimeout(this.timer)
        },
        mounted() {
          window.menuHandle = this.menuHandle
        },
        methods: {
          logout() {
            logoutApi().then((res)=>{
              if(res.code === 1){
                localStorage.removeItem('userInfo')
                window.location.href = '/backend/page/login/login.html'
              }
            })
          },
          goBack() {
            // window.location.href = 'javascript:history.go(-1)'
            const menu = this.menuList.find(item=>item.id===this.menuActived)
            // this.goBackFlag = false
            // this.headTitle = menu.name
            this.menuHandle(menu,false)
          },
          menuHandle(item, goBackFlag) {
            this.loading = true
            this.menuActived = item.id
            this.iframeUrl = item.url
            this.headTitle = item.name
            this.goBackFlag = goBackFlag
            this.closeLoading()
          },
          closeLoading(){
            this.timer = null
            this.timer = setTimeout(()=>{
              this.loading = false
            },1000)
          }
        }
      })
    </script>
  </body>
</html>

9、展示登录退出js

​ 展示登录退出功能请求的javascript文件

function loginApi(data) {
  return $axios({
    'url': '/employee/login',
    'method': 'post',
    data
  })
}

function logoutApi(){
  return $axios({
    'url': '/employee/logout',
    'method': 'post',
  })
}

10、过滤器拦截页面

​ 创建filter文件夹,在文件夹中创建登录拦截器LoginCheckFilter

/***
 * 检查用户是否已经完成登录
 */
@Slf4j
@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    //路径匹配器,支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        //1.获取本次请求的URL
        String requestURL = request.getRequestURI();

        log.info("拦截到请求:{}",request);

        //定义不需要处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };

        //2.判断本次请求是否需要处理
        boolean check = check(urls, requestURL);

        //3.如果不需要处理,则直接放行
        if (check){
            log.info("本次请求{}不需要处理:",requestURL);
            filterChain.doFilter(request,response);
            return ;
        }

        //4.判断登录状态,如果已登录,则直接放行
        if (request.getSession().getAttribute("employee")!=null){
            log.info("用户已登录,用户id为{}",request.getSession().getAttribute("employee"));
            filterChain.doFilter(request,response);
            return;
        }

        log.info("用户未登录");

        //5.如果未登录则返回未登录结果处
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return ;
    }

    /***
     * 路径匹配,检查本次是否需要放行
     */
    public boolean check(String[] urls,String requestURL){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url,requestURL);
            if (match){
                return true;
            }
        }
        return false;
    }
}

​ 创建完拦截器后,在"启动类"上添加注解@ServletComponentScan进行扫包

新增员工功能开发

1、展示添加页面

​ 展示add.html完整页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- 引入样式 -->
  <link rel="stylesheet" href="../../plugins/element-ui/index.css" />
  <link rel="stylesheet" href="../../styles/common.css" />
  <link rel="stylesheet" href="../../styles/page.css" />
</head>
<body>
  <div class="addBrand-container" id="member-add-app">
    <div class="container">
      <el-form
        ref="ruleForm"
        :model="ruleForm"
        :rules="rules"
        :inline="false"
        label-width="180px"
        class="demo-ruleForm"
      >
        <el-form-item label="账号:" prop="username">
          <el-input v-model="ruleForm.username" placeholder="请输入账号" maxlength="20"/>
        </el-form-item>
        <el-form-item
          label="员工姓名:"
          prop="name"
        >
          <el-input
            v-model="ruleForm.name"
            placeholder="请输入员工姓名"
            maxlength="20"
          />
        </el-form-item>

        <el-form-item
          label="手机号:"
          prop="phone"
        >
          <el-input
            v-model="ruleForm.phone"
            placeholder="请输入手机号"
            maxlength="20"
          />
        </el-form-item>
        <el-form-item
          label="性别:"
          prop="sex"
        >
          <el-radio-group v-model="ruleForm.sex">
            <el-radio label=""></el-radio>
            <el-radio label=""></el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item
          label="身份证号:"
          prop="idNumber"
        >
          <el-input
            v-model="ruleForm.idNumber"
            placeholder="请输入身份证号"
            maxlength="20"
          />
        </el-form-item>
        <div class="subBox address">
          <el-form-item>
            <el-button  @click="goBack()">
              取消
            </el-button>
            <el-button
              type="primary"
              @click="submitForm('ruleForm', false)"
            >
              保存
            </el-button>
            <el-button
              v-if="actionType == 'add'"
              type="primary"
              class="continue"
              @click="submitForm('ruleForm', true)"
            >
              保存并继续添加
            </el-button>
          </el-form-item>
        </div>
      </el-form>
    </div>
  </div>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="../../plugins/vue/vue.js"></script>
    <!-- 引入组件库 -->
    <script src="../../plugins/element-ui/index.js"></script>
    <!-- 引入axios -->
    <script src="../../plugins/axios/axios.min.js"></script>
    <script src="../../js/request.js"></script>
    <script src="../../api/member.js"></script>
    <script src="../../js/validate.js"></script>
    <script src="../../js/index.js"></script>
    <script>
      new Vue({
        el: '#member-add-app',
        data() {
          return {
            id: '',
            actionType : '',
            ruleForm : {
                'name': '',
                'phone': '',
                'sex': '男',
                'idNumber': '',
                username: ''
            }
          }
        },
        computed: {
          rules () {
            return {
              //账号
              username: [
                {
                  required: true, 'validator': checkUserName, trigger: 'blur'
                }
              ],
              //姓名
              name: [{ required: true, 'validator': checkName, 'trigger': 'blur' }],
              'phone': [{ 'required': true, 'validator': checkPhone, 'trigger': 'blur' }],
              'idNumber': [{ 'required': true, 'validator': validID, 'trigger': 'blur' }]
            }
          }
        },
        created() {
          this.id = requestUrlParam('id')
          this.actionType = this.id ? 'edit' : 'add'
          if (this.id) {
            this.init()
          }
        },
        mounted() {
        },
        methods: {
          async init () {
            queryEmployeeById(this.id).then(res => {
              console.log(res)
              if (String(res.code) === '1') {
                console.log(res.data)
                this.ruleForm = res.data
                this.ruleForm.sex = res.data.sex === '0' ? '女' : '男'
                // this.ruleForm.password = ''
              } else {
                this.$message.error(res.msg || '操作失败')
              }
            })
          },
          submitForm (formName, st) {
            this.$refs[formName].validate((valid) => {
              if (valid) {
                if (this.actionType === 'add') {
                  const params = {
                    ...this.ruleForm,
                    sex: this.ruleForm.sex === '女' ? '0' : '1'
                  }
                  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)
                  })
                } else {
                  const params = {
                    ...this.ruleForm,
                    sex: this.ruleForm.sex === '女' ? '0' : '1'
                  }
                  editEmployee(params).then(res => {
                    if (res.code === 1) {
                      this.$message.success('员工信息修改成功!')
                      this.goBack()
                    } else {
                      this.$message.error(res.msg || '操作失败')
                    }
                  }).catch(err => {
                    this.$message.error('请求出错了:' + err)
                  })
                }
              } else {
                console.log('error submit!!')
                return false
              }
            })
          },
          goBack(){
            window.parent.menuHandle({
              id: '2',
              url: '/backend/page/member/list.html',
              name: '员工管理'
            },false)
          }
        }
      })
    </script>
</body>
</html>

member.js

function getMemberList (params) {
  return $axios({
    url: '/employee/page',
    method: 'get',
    params
  })
}

// 修改---启用禁用接口
function enableOrDisableEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'put',
    data: { ...params }
  })
}

// 新增---添加员工
function addEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'post',
    data: { ...params }
  })
}

// 修改---添加员工
function editEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'put',
    data: { ...params }
  })
}

// 修改页面反查详情接口
function queryEmployeeById (id) {
  return $axios({
    url: `/employee/${id}`,
    method: 'get'
  })
}
2、创建异常类

​ 在common文件夹中创建全局异常类GlobalExceptionHandler

/***
 * 全局异常处理
 */
@ControllerAdvice(annotations = {RestController.class,Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
    /***
     * 异常处理方法
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
        log.error(ex.getMessage());
        if (ex.getMessage().contains("Duplicate entry")){
            String[] split = ex.getMessage().split(" ");
            String msg = "账号" + split[2] + "已存在";
            return R.error(msg);
        }

        return R.error("添加失败,出现了未知的异常");
    }
}
3、添加controller方法

​ 在employee中添加新增员工方法

    /***
     * 新增员工功能
     */
    @PostMapping
    public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
        log.info("新增员工,员工信息:{}",employee.toString());

        //设置初始化密码123456
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));

        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());

        //获取当前用户登录的id

        Long empId = (Long) request.getSession().getAttribute("employee");

        employee.setCreateUser(empId);
        employee.setUpdateUser(empId);

        employeeService.save(employee);

        return R.success("新增员工成功");
    }

员工信息分页查询

1、创建配置类拦截器
/**
 * 配置MP的分页插件
 */
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}
2、添加controller类方法

​ 在employeecontroller中添加分页功能

   /****
     *员工信息分页查询
     */
    @GetMapping("/page")
    public R<Page> page(int page,int pageSize,String name){
        log.info("page = {},pageSize = {},name = {}",page,pageSize,name);

        //构造分页构造器
        Page pageinfo = new Page(page,pageSize);

        //构造条件构造器
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        //添加过滤条件
        //模糊查询 like(是否使用该条件,字段,值)
        queryWrapper.like(StringUtils.isNotEmpty(name),Employee::getName,name);
        //添加排序条件
        queryWrapper.orderByD esc(Employee::getUpdateTime);

        //执行查询
        employeeService.page(pageinfo,queryWrapper);

        return R.success(pageinfo);
    }
3、展示员工信息页面

​ 展示完整list.html页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- 引入样式 -->
  <link rel="stylesheet" href="../../plugins/element-ui/index.css" />
  <link rel="stylesheet" href="../../styles/common.css" />
  <link rel="stylesheet" href="../../styles/page.css" />
  <style>
    #member-app  .notAdmin::after{
      border: 0 !important;

    }
  </style>
</head>
<body>
  <div class="dashboard-container" id="member-app">
    <div class="container">
      <div class="tableBar">
        <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>
        <el-button
          type="primary"
          @click="addMemberHandle('add')"
        >
          + 添加员工
        </el-button>
      </div>
      <el-table
        :data="tableData"
        stripe
        class="tableBox"
      >
        <el-table-column
          prop="name"
          label="员工姓名"
        ></el-table-column>
        <el-table-column
          prop="username"
          label="账号"
        ></el-table-column>
        <el-table-column
          prop="phone"
          label="手机号"
        ></el-table-column>
        <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>
          </template>
        </el-table-column>
      </el-table>
      <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>
    </div>
  </div>
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="../../plugins/vue/vue.js"></script>
    <!-- 引入组件库 -->
    <script src="../../plugins/element-ui/index.js"></script>
    <!-- 引入axios -->
    <script src="../../plugins/axios/axios.min.js"></script>
    <script src="../../js/request.js"></script>
    <script src="../../api/member.js"></script>
    <script>
      new Vue({
        el: '#member-app',
        data() {
          return {
             input: '',
             counts: 0,
             page: 1,
             pageSize: 10,
             tableData : [],
             id : '',
             status : '',
          }
        },
        computed: {},
        created() {
          this.init()
          this.user = JSON.parse(localStorage.getItem('userInfo')).username
        },
        mounted() {
        },
        methods: {
          async init () {
            const params = {
              page: this.page,
              pageSize: this.pageSize,
              name: this.input ? this.input : undefined
            }
            await getMemberList(params).then(res => {
              if (String(res.code) === '1') {
                this.tableData = res.data.records || []
                this.counts = res.data.total
              }
            }).catch(err => {
              this.$message.error('请求出错了:' + err)
            })
          },
          handleQuery() {
            this.page = 1;
            this.init();
          },
           // 添加
          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)
            }
          },
          //状态修改
          statusHandle (row) {
            this.id = row.id
            this.status = row.status
            this.$confirm('确认调整该账号的状态?', '提示', {
              'confirmButtonText': '确定',
              'cancelButtonText': '取消',
              'type': 'warning'
              }).then(() => {
              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)
              })
            })
          },
          handleSizeChange (val) {
            this.pageSize = val
            this.init()
          },
          handleCurrentChange (val) {
            this.page = val
            this.init()
          }
        }
      })
    </script>
</body>
</html>

启用/禁用员工账号

1、前端展示页面

​ 展示list.html部分页面,“员工信息分页查询”中包含完整员工信息页面

<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>
          </template>
        </el-table-column>

​ 展示list.html页面中的vue代码,在钩子函数中,获取员工姓名

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

​ 展示list.html页面中的vue代码,在方法中,修改员工状态。当要修改状态时,将状态修改为当前相反的状态。

//状态修改
          statusHandle (row) {
            this.id = row.id
            this.status = row.status
            this.$confirm('确认调整该账号的状态?', '提示', {
              'confirmButtonText': '确定',
              'cancelButtonText': '取消',
              'type': 'warning'
              }).then(() => {
              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)
              })
            })

​ 展示ajax代码

// 修改---启用禁用接口
function enableOrDisableEmployee (params) {
  return $axios({
    url: '/employee',
    method: 'put',
    data: { ...params }
  })
}
2、添加controller类方法
/**
 * 根据id修改员工信息
 */
@PutMapping
public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
    log.info(employee.toString());

    Long employeeId = (Long)request.getSession().getAttribute("employee");
    employee.setUpdateTime(LocalDateTime.now());
    employee.setUpdateUser(employeeId);
    employeeService.updateById(employee);

    return R.success("员工信息修改成功");
}

3、创建对象映射器

​ 在使用json将前端数据传递到后端页面的时候,整型值的精度会丢失,所以需要将值转换成字符串类型。所以需要将数值进行序列化与反序列化。

/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);


        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }

编辑员工信息功能开发

获取当前页面中的url,在url中得到主页转递过来的id值,并将id作为参数,请求后端的数据

1.前端页面展示

​ 展示前端页面list.html和add.html

list.html

<el-button
              type="text"
              size="small"
              class="blueBug"
              @click="addMemberHandle(scope.row.id)"
              :class="{notAdmin:user !== 'admin'}"
            >
              编辑
            </el-button>
 // 添加
          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)
            }
          },

add.html

created() {
          this.id = requestUrlParam('id')
          this.actionType = this.id ? 'edit' : 'add'
          if (this.id) {
            this.init()
          }
        },	
2.添加controller方法
/**
     * 根据id查询员工信息
     */
    @GetMapping("/{id}") 
    public R<Employee> getById(@PathVariable Long id){
        log.info("根据id查询用户信息");

        Employee employee = employeeService.getById(id);
        if (employee!=null){
            return R.success(employee);
        }
        return R.error("员工信息查询失败");
    }

插入更新公共字段自动填充

​ mybatis plus 公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码

1.添加注解

​ 在实体类Employee类的属性上,加入@TableField注解,指定自动填充的策略

@TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)
    private Long createUser;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
2.实现接口类

​ 按照框架要求编写元数据对象处理器MyMetaObjecthandler公共类,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口

/**
 * 自定义元数据对象处理器
 */
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充[insert]....");
        log.info(metaObject.toString());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充[update]....");
        log.info(metaObject.toString());
    }
}

优化employeecontroller类,将save方法代码注释

/*
employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        //获取当前用户登录的id
        Long empId = (Long) request.getSession().getAttribute("employee");
        employee.setCreateUser(empId);
        employee.setUpdateUser(empId);
*/
//此功能放到公共类中
        /*
        Long employeeId = (Long)request.getSession().getAttribute("employee");
        employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(employeeId);
        */

​ 在实现类中补全代码

@Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充[insert]....");
        log.info(metaObject.toString());
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("createUser",new Long(1));
        metaObject.setValue("updateUser",new Long(1));
    }
  @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充[update]....");
        log.info(metaObject.toString());
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("updateUser",new Long(1));
    }
3.线程

​ 在学习ThreadLocal之前,我们需要先确认一个事情,就是客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一个线程:

  • LoginCheckFilter的doFilter方法
  • EmployeeController的update
  • MyMetaObjectHandler的updateFill方法
  • 可以在以上三个方法中,添加测试
long id = Thread.currentThread().getId();
log.info("线程{}",id);

什么是ThreadLocal?

ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用Thread变量时,ThreadLocal为每个使用该变量线程提供的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。ThreadLocal为每个线程提供一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

ThreadLocal常用方法:

  • public void set(T value) 设置当前线程的线程局部变量的值
  • public T get() 返回当前线程所对应的线程局部变量的值

我们可以在LoginCheckFilter方法中获取当前登录用户id,并调用ThreadLocald的set方法来设置当前线程的线程局部变量的值(用户id),然后再MyMetaObjectHandler的updateFill方法中调用ThreadLocal

实现步骤:

1、编写BaseContext工具类,基于ThreadLocal封装的工具类

2、在LoginCheckFilter的方法中调用BaseContext来设置当前登录用户的id

3、在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id

ps:ThreadLocal只能存储一个数值,即一个线程一个local

4.创建封装工具类

​ 在公共类中新建BaseContext工具类

/**
 * 基于Thread封装工具类,用户保存和获取当前登录用户id
 */
public class BaseContext {
    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    public static void setThreadLocal(Long id){
        threadLocal.set(id);
    }

    public static Long getCuttentId(){
        return threadLocal.get();
    }
}
5.修改登录检查过滤器

​ 在LoginCkeckFilter里添加代码

//4.判断登录状态,如果已登录,则直接放行
        if (request.getSession().getAttribute("employee")!=null){
            log.info("用户已登录,用户id为{}",request.getSession().getAttribute("employee"));

            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setThreadLocal(empId);

            filterChain.doFilter(request,response);
            return;
        }
6.修改自定义元数据对象处理器
在MyMetaObjecthandler类自动填充字段的插入更新字段功能中,修改其中的代码
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("createUser",BaseContext.getCuttentId());
        metaObject.setValue("updateUser",BaseContext.getCuttentId());
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("updateUser",BaseContext.getCuttentId());

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值