刷新页面维持用户登录状态功能
//判断是否登录的方法 isLogin() { jQuery.getJSON("/user/islogin", {}, function (result) { if (result != null && result.data != null && result.data.id > 0) { //此时确定为登录状态,点击刷新后应该仍在主页面,而不是需要重新登录 app.login.isLogin = true; //设置欢迎信息 app.login.inputUsername = result.data.username; //请求后端得到菜品列表 jQuery.getJSON("/dish/list", {}, function (result) { if (result != null && result.data != null) { app.dishes = result.data; } }); } }); },
注意要在最后调用这个isLogin方法
controller层
/** * 判断登录状态 * * @param request * @return */ @RequestMapping("/islogin") public ResponseBody<UserInfo> isLogin(HttpServletRequest request) { UserInfo user = null; HttpSession session = request.getSession(false); if (session != null && session.getAttribute(AppFinal.USERINFO_SESSION_KEY) != null) { user = (UserInfo) session.getAttribute(AppFinal.USERINFO_SESSION_KEY); } return new ResponseBody<UserInfo>(0, "", user); }
isLogin方法是判断当前登录后,刷新页面不退出登录状态,最后在app外面调用了这个isLogin方法
原理是每次vue刷新页面会把就是会把变量如果改变了再全部变为初始值,所以如果我们不去设置isLogin方法的话,最终当我门点击刷新后,isLogin这个属性重新置为false,那么就会显示我们的导航栏了:因为!login.isLogin为true,
退出登录功能
前端
<v-list-item link v-on:click="logout"> <v-list-item-action> <v-icon>mdi-logout</v-icon> </v-list-item-action> <v-list-item-content> <v-list-item-title>退出登录</v-list-item-title> </v-list-item-content> </v-list-item>
//退出登录功能 logout() { if (confirm("是否确认退出?")) { jQuery.getJSON("/user/logout", {}, function (result) { if (result != null && result.data != null && result.data > 0) { //退出成功 alert("退出成功"); //刷新当前页面 location.href = location.href; } else { alert("抱歉,操作失败,请重试"); } }); } },
controller
/** * 退出登录 * @param request * @return */ @RequestMapping("/logout") public ResponseBody<Integer> logOut(HttpServletRequest request) { int data = 0; HttpSession session = request.getSession(false); if (session != null && session.getAttribute(AppFinal.USERINFO_SESSION_KEY) != null) { session.removeAttribute(AppFinal.USERINFO_SESSION_KEY); data = 1; } session.removeAttribute(AppFinal.USERINFO_SESSION_KEY); return new ResponseBody<>(0, "", data); }
用户下单菜品功能
<v-col :cols="2"> <v-btn color="primary" block v-on:click="dishSubmit">下单</v-btn> </v-col> dishSubmit() { if (confirm("确实提交?")) { let dids = "";//菜品id集合 app.dishes.forEach(dish => { //选中了菜品就加1 if (dish.isSelected) { dids += (dish.id + ","); } }); if (dids != null) { //dids不等于null说明菜品已经下单提交,请求后端实现点餐 //传递的参数为菜品id的集合 jQuery.getJSON("/order/add", {"dids": dids}, function (result) { if (result != null && result.data != null && result.data > 0) { alert("恭喜:点餐成功"); } }); } else { alert("请先选中菜品信息"); } } },
controller层
(1)菜品信息插入到OrderInfo表中
首先拿到下单的用户uid,从而拿到对应的订单oid
(2)中间表OrderDetail也要完成订单与菜品的关联
将之前拿到的订单oid与前端传来的菜品dids进行关联
@RequestMapping("/add") public ResponseBody<Integer> addOrder(String dids, HttpServletRequest request) { int data = 0; //1:添加订单信息,返回一个订单ID //(1):要想添加订单信息,第一步是获取用户的id,因为在订单列表中,用户id是订单表的逻辑外键 //我们是要通过用户的id来拿到对应的订单id // 如果当前Session没有就为null HttpSession session = request.getSession(false); if (session != null && session.getAttribute(AppFinal.USERINFO_SESSION_KEY) != null) { int uid = ((UserInfo) session.getAttribute(AppFinal.USERINFO_SESSION_KEY)).getId(); //(2):将用户的id存入到OrderInfo OrderInfo orderInfo = new OrderInfo(); orderInfo.setUid(uid); //订单添加方法 //result代表添加影响的行数 //addOrder方法会获取到订单表的自增id int result = orderInfoMapper.addOrder(orderInfo); //result大于0说明插入成功,因为在使用MyBatis做持久层时,insert语句默认是不返回记录的主键值,而是返回插入的记录条数 if (result > 0) { //2:添加订单详情,需要用到订单ID data = orderDetailMapper.add(orderInfo.getId(), dids.split(",")); } } return new ResponseBody<>(0, "", data); }
mapper层
//添加订单 public int addOrder(OrderInfo orderInfo); //传参的时候订单的id可能只有一个,但是菜品的id可能有多个,所以用String数组来表示 public int add(int oid,String[] dids);
xml
<!--注意此处插入时需要获取自增id,则需要使用useGeneratedKeys和keyProperty keyProperty会将得到的主键id值赋给对应表中的主键,useGeneratedKeys为是否使用自动主键,肯定值为true--> <insert id="addOrder" useGeneratedKeys="true" keyProperty="id"> insert into orderinfo(uid,status) values(#{uid},0) </insert>
注意:在使用MyBatis做持久层时,insert语句默认是不返回记录的主键值,而是返回插入的记录条数
<!--使用foreach来遍历我们前台传过来的菜单id这个集合,将集合中的每一条数据存入到did中 oid代表订单id,订单id只有一个,但是菜单id可以有很多个,因为每个菜品都对应一个id --> <!--因为我们要往did中插入的菜单id是一个集合,所以我们可以使用foreach循环,collection是 集合,我们放入的是add方法的参数:数组dids,item代表列,separator代表分隔符,意思就是 values后面是(),(),()这样的形式--> <insert id="add"> insert into orderdetail(oid,did) values <foreach collection="dids" item="item" separator=","> (#{oid},#{item}) </foreach> </insert>
用户查看订单详情功能
前端
显示用户自身订单状态代码
<v-list-item link v-on:click="orderList"> <v-list-item-action> <v-icon>mdi-cart-outline</v-icon> </v-list-item-action> <v-list-item-content> <v-list-item-title>我的订单</v-list-item-title> </v-list-item-content> </v-list-item> orderList() { jQuery.getJSON("/order/list", {}, function (result) { if (result != null && result.data != null) { //获取订单列表成功 app.orders = result.data; app.status = 'ordersPage'; } else { //获取订单失败 alert("获取订单列表失败,请重试"); } }); }, <!-- 个人订单列表 --> <v-simple-table v-show="status == 'ordersPage' && login.isLogin"> <template v-slot:default> <thead> <tr> <th class="text-left">序号</th> <th class="text-left">状态</th> <th class="text-left">时间</th> <th class="text-left">详情</th> </tr> </thead> <tbody> <tr v-for="order in orders"> <td>{{order.id}}</td> <!--三元运算符在vue中可以使用--> <td>{{order.status==0?'未完成':'已完成'}}</td> <td>{{order.createtime}}</td> <td> <v-btn color='primary' v-on:click="detail(order.id)">查看详情</v-btn> </td> </tr> </tbody> </template> </v-simple-table>
查看详情的代码:
<!-- 某个订单的详情 --> <v-row justify="center"> <v-dialog v-model="showCurOrder" persistent max-width="600px"> <v-simple-table> <template v-slot:default> <thead> <tr> <th class="text-left">菜品</th> <th class="text-left">价格</th> </tr> </thead> <tbody> <tr v-for="dish in curOrder"> <td>{{dish.dish.name}}</td> <td>{{dish.dish.price}}</td> </tr> <tr> <td>总金额:{{curOrderMoney()}}</td> <td> <v-btn color="primary" v-on:click="showCurOrder = false">关闭</v-btn> </td> </tr> </tbody> </template> </v-simple-table> </v-dialog> </v-row> //计算某个订单详情的总钱数 curOrderMoney() { let money = 0; app.curOrder.forEach(order => { money += order.dish.price; }); return money; }, //查看订单详情 detail(oid) { jQuery.getJSON("/detail/list", {"oid": oid}, function (result) { if (result != null && result.data != null) { app.curOrder = result.data; //显示订单详情页面 app.showCurOrder = true; } }); },
controller
显示订单代码
/** * 用户获取订单列表 * * @return */ @RequestMapping("/list") public ResponseBody<List<OrderInfo>> getList(HttpServletRequest request) { List<OrderInfo> orderinfo = null; HttpSession session = request.getSession(false); int uid = 0; if (session != null && session.getAttribute(AppFinal.USERINFO_SESSION_KEY) != null) { //获取用户id uid = ((UserInfo) session.getAttribute(AppFinal.USERINFO_SESSION_KEY)).getId(); orderinfo = orderInfoMapper.list(uid); } return new ResponseBody<>(0, "", orderinfo); }
查看详情代码:
@RequestMapping("/list") public ResponseBody<List<OrderDetail>> getList(int oid){ List<OrderDetail> list = orderDetailMapper.getList(oid); return new ResponseBody<>(0,"",list); }
mapper接口
显示订单代码
//获取订单列表 public List<OrderInfo> list(int uid);
查看详情代码
public List<OrderDetail> getList(int oid);
xml
显示订单代码
<select id="list" resultType="com.example.ordersys.model.OrderInfo"> select * from orderinfo where uid=#{uid} </select>
查看详情代码
这里要注意,先来看订单详情表的实体类设计:
package com.example.ordersys.model; import lombok.Data; /** * @author SongBiao * @Date 2021/1/18 */ @Data public class OrderDetail { private int oid; private int did; private Dish dish; }
可以看到OrderDetail实体类中还有一个Dish对象,但是注意这个对象在我们表中是没有的,原因是当我们用户查看订单详情的时候,需要查看菜品的详细信息以及总价格,我们直接再这里定义一个对象后,后期通过连表查询,然后将查询的结果包装成一个Dish类对象即可。那么我们再xml中就需要使用resultMap来指定对应的映射关系,
首先再DishMapper.xml中指定dish表对应的实体类的映射关系;
<resultMap id="BaseResultMap" type="com.example.ordersys.model.Dish"> <id column="id" property="id"></id> <result column="name" property="name"></result> <result column="price" property="price"></result> </resultMap>
同时再OrderDetailMapper.xml中指定OrderDetail实体类的映射关系,注意需要使用到association属性来表示一对一的关系,因为后期我们使用连表查询中,orderdetail与dish进行左连接后,查询出来的记录是一行一行的,也就是说一个订单编号对应一个菜品名称,而不是多个
<resultMap id="BaseResultMap" type="com.example.ordersys.model.OrderDetail"> <result property="did" column="did"></result> <result property="oid" column="oid"></result> <association property="dish" columnPrefix="d_" resultMap="com.example.ordersys.mapper.DishMapper.BaseResultMap"> </association> </resultMap> <select id="getList" resultMap="BaseResultMap"> select o.*,d.name d_name,d.price d_price from orderdetail o left join dish d on o.did=d.id where o.oid=#{oid} </select>
mysql> select o.*,d.name d_name,d.price d_price from orderdetail o left join dish d on o.did=d.id -> where o.oid=16; +-----+-----+--------+---------+ | did | oid | d_name | d_price | +-----+-----+--------+---------+ | 3 | 16 | 红烧肉 | 30 | | 4 | 16 | 茄子 | 25 |
这里就可以显示出订单编号为16的用户点了两个菜,其菜品编号为3,4,菜品名称为红烧肉,茄子,价格分别为30,25
注意这里我们使用了连表查询(左连接)
商家模块
商家登录功能
前端
<!-- 左侧导航 --> <v-navigation-drawer v-model="drawer" app> <v-list dense v-show="!login.isLogin"> <v-list-item link v-on:click="login.showLoginDialog=true"> <v-list-item-action> <v-icon>mdi-login</v-icon> </v-list-item-action> <v-list-item-content> <v-list-item-title>登录</v-list-item-title> </v-list-item-content> </v-list-item> </v-list> <v-row justify="center"> <v-dialog v-model="login.showLoginDialog" persistent max-width="400px"> <v-card> <v-card-title> <span class="headline">登录</span> </v-card-title> <v-card-text> <v-container> <v-row> <v-col cols="12" sm="12"> <v-text-field label="用户名*" v-model="login.inputUsername" required> </v-text-field> </v-col> </v-row> <v-row> <v-col cols="12" sm="12"> <v-text-field label="密码*" v-model="login.inputPassword" required> </v-text-field> </v-col> </v-row> <v-row> <v-col cols="12" sm="6"> <v-btn color="primary" block v-on:click="userLogin">登录</v-btn> </v-col> <v-col cols="12" sm="6"> <v-btn color="primary" block v-on:click="login.showLoginDialog=false">取消 </v-btn> </v-col> </v-row> </v-container> </v-card-text> </v-card> </v-dialog> </v-row> userLogin() { let username = app.login.inputUsername; let password = app.login.inputPassword; //非空校验 if (username == "") { alert("请输入用户名"); return false; } if (password == "") { alert("请输入密码"); return false; } //访问后端接口,验证用户信息 jQuery.getJSON("/user/login", { "username": username, "password": password }, function (result) { if (result != null && result.data != null && result.data.id > 0) { //等于1,说明是管理员登录 if (result.data.isadmin == 1) { //登录成功 alert("登录成功!"); //登录成功后,隐藏左侧未登录之前的导航,并显示欢迎信息 //因为两者的v-show是一样的 app.login.isLogin = true; //去掉登录时的窗口,还是查看v-model app.login.showLoginDialog = false; //请求后端得到菜品列表 jQuery.getJSON("/dish/list", {}, function (result) { if (result != null && result.data != null) { app.dishes = result.data; } }); } else { alert("非法操作,权限不足"); } } else { //用户名或密码错误,请重新输入 alert("用户名或密码错误,请重新输入"); } }); },
controller
//登录功能 @RequestMapping("/login") public ResponseBody<UserInfo> login(UserInfo userInfo, HttpServletRequest request) { UserInfo user = userMapper.login(userInfo); //登录后将信息存入session当中 if (user != null && user.getId() > 0) { HttpSession session = request.getSession(); session.setAttribute(AppFinal.USERINFO_SESSION_KEY, user); } return new ResponseBody<>(0, "", user); } @RequestMapping("/list") public ResponseBody<List<Dish>> getList() { List<Dish> data = dishMapper.getDishList(); return new ResponseBody<>(0, "", data); }
mapper
//登录方法 public UserInfo login(UserInfo userInfo); //查询菜单列表 public List<Dish> getDishList();
xml
<select id="getDishList" resultType="com.example.ordersys.model.Dish"> select * from dish </select> <select id="login" resultType="com.example.ordersys.model.UserInfo"> select * from userinfo where username=#{username} and password=#{password} </select>
保持登录状态
前端
//保持登录状态的方法 isLogin() { jQuery.getJSON("/user/islogin", {}, function (result) { if (result != null && result.data != null && result.data.id > 0) { if (result.data.isadmin == 1) { //等于1是超级管理员 //此时确定为登录状态,点击刷新后应该仍在主页面,而不是需要重新登录 app.login.isLogin = true; //设置欢迎信息 app.login.inputUsername = result.data.username; //请求后端得到菜品列表 jQuery.getJSON("/dish/list", {}, function (result) { if (result != null && result.data != null) { app.dishes = result.data; } }); } } }); },
在new Vue外部调用isLogin方法
controller
@RequestMapping("/islogin") public ResponseBody<UserInfo> isLogin(HttpServletRequest request) { UserInfo user = null; HttpSession session = request.getSession(false); if (session != null && session.getAttribute(AppFinal.USERINFO_SESSION_KEY) != null) { user = (UserInfo) session.getAttribute(AppFinal.USERINFO_SESSION_KEY); } return new ResponseBody<UserInfo>(0, "", user); }
菜品添加功能
前端
<v-row v-show="status == 'dishesPage' && login.isLogin"> <v-col :col="11"> </v-col> <v-col :cols="2"> <v-btn color="primary" block v-on:click="showAddDish = true">新增菜品</v-btn> </v-col> </v-row> <!-- 新增菜品 --> <v-row justify="center"> <v-dialog v-model="showAddDish" persistent max-width="600px"> <v-card> <v-card-title> <span class="headline">新增菜品</span> </v-card-title> <v-card-text> <v-container> <v-row> <v-col cols="12" sm="12"> <v-text-field label="菜品名*" v-model="newDish.name" required></v-text-field> </v-col> </v-row> <v-row> <v-col cols="12" sm="12"> <v-text-field label="价格(元)*" v-model="newDish.price" required> </v-text-field> </v-col> </v-row> <v-row> <v-col cols="12" sm="6"> <v-btn color="primary" block v-on:click="addDish()">新增</v-btn> </v-col> <v-col cols="12" sm="6"> <v-btn color="primary" block v-on:click="showAddDish = false">取消</v-btn> </v-col> </v-row> </v-container> </v-card-text> </v-card> </v-dialog> </v-row> //新增菜品 addDish() { if (confirm("确认是否提交?")) { let name = app.newDish.name; let price = app.newDish.price; // if (name == "") { alert("请先输入菜品名称!"); return false; } if (price == "" || price == 0) { alert("请先输入价格!"); return false; } jQuery.getJSON("/dish/add", {"name": name, "price": price}, function (result) { if (result != null && result.data != null && result.data > 0) { //data>0表示添加成功 alert("恭喜:菜品添加成功!"); //每次添加完菜品后自动刷新 location.href = location.href; } else { alert("抱歉:操作失败请重试!"); } }); } },
controller
//管理员添加菜品的后台方法 @RequestMapping("/add") public ResponseBody<Integer> addDish(Dish dish) { int data = dishMapper.addDish(dish); return new ResponseBody<>(0, "", data); }
mapper
public int addDish(Dish dish);