瑞吉点餐项目系列


java
springboot
mybatisplus
mysql
element-ui
vue
axios
html
css
maven

瑞吉项目流程图

登录流程

在这里插入图片描述

登录流程实现细节

登录主页
在这里插入图片描述
###登录页面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="../../favicon1.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') {
                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>

当点击登录按钮时调用handleLogin方法

              @click.native.prevent="handleLogin">
              <span v-if="!loading">登录</span>
              <span v-else>登录中...</span>
      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') {
                localStorage.setItem('userInfo',JSON.stringify(res.data))
                window.location.href= '/backend/index.html'
              } else {
                this.$message.error(res.msg)
                this.loading = false
              }
            }
          })
        }
      }

登录成功后跳转到/backend/index.html

管理页面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="favicon1.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>

登录相关 java后台代码实现

前端发送请求

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

后台通过全局过滤器判断登录

package com.jjk.ruiji.filters;

import com.alibaba.fastjson.JSON;
import com.jjk.ruiji.common.BaseContext;
import com.jjk.ruiji.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//全局过滤器
@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;
        //A. 获取本次请求的URI
        String requestURI = request.getRequestURI();
        String urls[] = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/common/**"
        };
        //B. 判断本次请求, 是否需要登录, 才可以访问
        //C. 如果不需要,则直接放行
        if (check(urls, requestURI)) {
            filterChain.doFilter(request, response);
            return;
        }
        //D. 判断登录状态,如果已登录,则直接放行
        if (request.getSession().getAttribute("employee") != null) {
            Long empId = (Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(empId);
            filterChain.doFilter(request, response);
            return;
        }
  /*      Long id = Thread.currentThread().getId();
        log.info("id {}", id);*/
        //E. 如果未登录, 则返回未登录结果
        log.info("用户未登录");
        //5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
    }

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

登录成功放行来到 EmployeeController 处理登录请求

    @PostMapping("/login")
    public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee) {
        //①. 将页面提交的密码password进行md5加密处理, 得到加密后的字符串
        String password = employee.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        //②. 根据页面提交的用户名username查询数据库中员工数据信息
        LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Employee::getUsername, employee.getUsername());
        Employee emp = employeeService.getOne(wrapper);
        //③. 如果没有查询到, 则返回登录失败结果
        if (emp == null) {
            return R.error("登录失败");
        }
        //④. 密码比对,如果不一致, 则返回登录失败结果
        if (!emp.getPassword().equals(password)) {
            return R.error("登录失败");
        }
        //⑤. 查看员工状态,如果为已禁用状态,则返回员工已禁用结果
        if (emp.getStatus() == 0) {
            return R.error("员工已禁用");
        }
        //⑥. 登录成功,将员工id存入Session, 并返回登录成功结果
        request.getSession().setAttribute("employee", emp.getId());
        System.out.println(emp.toString());
        return R.success(emp);
    }

    @PostMapping("/logout")
    public R<String> logout(HttpServletRequest request) {
        request.getSession().removeAttribute("employee");
        return R.success("退出成功");
    }

登录成功然后进入管理页面
在这里插入图片描述

失败返回登录页面并提示信息
在这里插入图片描述
管理页面默认初始化第一个为员工管理页

员工管理

在这里插入图片描述

员工管理代码实现

添加员工
前端发送添加员工请求

1 点击按钮调用方法 addMemberHandle

        <el-button
          type="primary"
          @click="addMemberHandle('add')"
        >
          + 添加员工

2 执行方法 跳转到 url: ‘/backend/page/member/add.html’,

     // 添加
          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)
            }
          }

3 编辑号信息后 点击保存按钮 调用 方法 @click=“submitForm(‘ruleForm’, true)”

              保存
            </el-button>
            <el-button
              v-if="actionType == 'add'"
              type="primary"
              class="continue"
              @click="submitForm('ruleForm', true)"
            >

4 执行方法 submitForm (formName, st)

  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
              }
            })
          },

5 发送添加请求 到后台

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

6 请求数据通过全局过滤器后 来到EmployeeController
然后处理数据 添加成功后 响应数据并刷新页面

    @PostMapping
    public R<String> save(@RequestBody Employee employee) {
        log.info("添加员工 {}", employee.toString());
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        employeeService.save(employee);
        return R.success("新增员工成功");
    }

按姓名搜索员工代码实现

1 点击按钮触发 @keyup.enter.native=“handleQuery”

     <el-input
          v-model="input"
          placeholder="请输入员工姓名"
          style="width: 250px"
          clearable
           @keyup.enter.native="handleQuery"
        >

2 执行方法 handleQuery()

    handleQuery() {
            this.page = 1;
            this.init();
          }

3 执行方法 init()

    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)
            })
          }

4 执行方法 getMemberList () 发送请求到后台

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

5 请求通过全局过滤器来到 EmployeeController
处理数据 并响应数据

    /**
     * 员工信息分页查询
     *
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page<Employee>> page(int page, int pageSize, String name) {
        /* 分页查询
           底层基于mybatisplus 提供的一个分页插件
           编写格式固定死的
         */
        log.info("page = {} pageSize = {} name = {}", page, pageSize, name);
        //分页构造器
        Page<Employee> pageInfo = new Page<>(page, pageSize);
        //条件构造器
        LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper();
        //添加一个过滤条件
        lambdaQueryWrapper.like(StringUtils.isNotEmpty(name), Employee::getName, name);
        //添加排序条件
        lambdaQueryWrapper.orderByDesc(Employee::getUpdateTime);
        //执行查询
        employeeService.page(pageInfo, lambdaQueryWrapper);
        return R.success(pageInfo);
    }

修改员工代码实现

1

             <el-button
              type="text"
              size="small"
              class="blueBug"
              @click="addMemberHandle(scope.row.id)"
              :class="{notAdmin:user !== 'admin'}"
            >
              编辑

2

    // 添加
          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)
            }
          }

3

     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 || '操作失败')
              }
            })
          }

4

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

5

    /**
     * 根据id查询
     *
     * @param id
     * @return
     */
    @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("查询id对应员工失败");
    }

启用或禁用员工代码实现

1

分类管理

在这里插入图片描述

菜品管理

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色猫咪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值