博客目录:
......
十、基于Django mysql的点餐系统设计-第十篇(H5手机移动端点餐:登录)
十一、基于Django mysql的点餐系统设计-第十一篇(H5手机移动端点餐:购物车操作)
十二、基于Django mysql点餐系统设计-第十二篇(移动端:会员下单处理)
本章源码下载地址:https://github.com/hopeSuceess/testorder/tree/testorder_20220629_01
购物车下完单后要进行展示,本次来编写订单的展示。首先订单展示在前端页面哪呢?根据设计会将订单展示放在"我的-我的订单"下。现在点击"我的",还没有反应,需要先实现"我的"模块功能。
在mobile/urls.py中创建个人中心路由
#会员中心
path('member',member.index, name="mobile_member_index"), #会员中心首页
在mobile/views下创建member.py并创建index函数
def index(request):
'''个人中心首页'''
return render(request, "mobile/member.html")
在templates/mobile下创建member.html,页面上的返回键有两种方式,一种是加个"个人中心"标头,返回键调用API接口;另一种方式是通过href标签的方式返回制定的页面
第一种展现方式:点击返回后,返回到上一页即首页:
......
{% block mainbody %}
<!--头部-->
<header>
<div class="header">
<h1>个人中心</h1>
<a href="javascript:window.history.back();" class="return"><i></i></a>
</div>
</header>
<!--内容区-->
<article class="main-container">
<div class="member-header">
<p> </p>
<div class="memberhead"><img src="{% static 'mobile/img/head03.jpg' %}" alt=""><i class="icon-woman"></i></div>
<ul>
......
第二种展现方式:点击返回后,返回到上一页即首页:
{% block mainbody %}
<!--内容区-->
<article class="main-container">
<div class="member-header">
<button class="button" onclick="location.href='{% url 'mobile_index' %}'"><span class="return"><i></i></span></button>
<p> </p>
<div class="memberhead"><img src="{% static 'mobile/img/head03.jpg' %}" alt=""><i class="icon-woman"></i></div>
<ul>
......
两种表现形式,大家可根据喜好选择。member.html完整代码如下
{% extends 'mobile/base.html' %}
{% load static %}
{% block mainbody %}
<!--头部-->
<header>
<div class="header">
<h1>个人中心</h1>
<a href="javascript:window.history.back();" class="return"><i></i></a>
</div>
</header>
<!--内容区-->
<article class="main-container">
<div class="member-header">
{# <button class="button" onclick="location.href='{% url 'mobile_index' %}'"><span class="return"><i></i></span></button>#}
<p> </p>
<div class="memberhead"><img src="{% static 'mobile/img/head03.jpg' %}" alt=""><i class="icon-woman"></i></div>
<ul>
<li class="line-w"><b> </b><h1></h1></li>
</ul>
<div class="waves1 icon-waves"></div>
<div class="waves2 icon-waves"></div>
<div class="waves3 icon-waves"></div>
</div>
<div class="list">
<ul class="line">
<li onclick="location.href='{% url 'mobile_member_orders' %}'"><i class="icon-allorders"></i>我的订单<span class="icon-arrowright"></span></li>
</ul>
<ul class="line">
<li onclick="location.href='member-data.html'"><i class="icon-head"></i>我的资料<span class="icon-arrowright"></span></li>
<li onclick="location.href='member-pwd.html'"><i class="icon-pwd"></i>修改密码<span class="icon-arrowright"></span></li>
<li onclick="location.href='member-coupon.html'"><i class="icon-coupons"></i>优惠券<span class="icon-arrowright"></span></li>
<li onclick="location.href='member-Addrees.html'"><i class="icon-address"></i>收货地址<span class="icon-arrowright"></span></li>
</ul>
<ul class="line">
<li onclick="location.href='{% url 'mobile_member_logout' %}'"><i class="icon-out"></i>退出</li>
</ul>
</div>
</article>
{% endblock %}
到这"我的"模块已经编写完成,启动工程,访问http://127.0.0.1:8000/mobile/ ,点击"我的",跳转到个人中心页面
接下来开始实现个人中心-我的订单功能。到现在为止,咱们实现了很多功能了,所有的功能都是在Django框架下,遵从MTV思想和路由控制器实现的。现在要说的"我的订单"功能也不例外,在这里就不事无巨细的叙述了。重点说下view层后端代码的实现逻辑和template层前端代码的实现逻辑。
先来说下view层后端的代码逻辑,想要实现的功能是什么呢?可以通过该会员获取其下的订单信息,前端如果点击不同状态的订单状态可以获取对应订单状态下的订单,每个展示的订单只展示前4条订单详情。针对以上需求,开发人员该怎么办呢?看以下代码编写
def orders(request):
'''个人中心浏览订单'''
mid = request.session['mobileuser']['id'] #获取当前会员id号
olist = Orders.objects.filter(member_id=mid)
# 获取、判断并封装状态status搜索条件
status = request.GET.get('status', '')
if status != '':
olist = olist.filter(status=status)
# 按id号做降序排序
list2 = olist.order_by("-id")
order_status = ["无","排队中","已撤销","已完成"]
# 遍历订单,关联其他表数据(订单详情,店铺信息)
for vo in list2:
plist = OrderDetail.objects.filter(order_id=vo.id)[:4] # 获取前4条
vo.plist = plist
vo.statusinfo = order_status[vo.status] # 转换订单状态
return render(request, "mobile/member_orders.html",{"orderslist":list2})
咱们来分析下上面的代码:通过request.session['mobileuser']['id']在缓存中获取到该会员id号,再用Orders.objects.filter(member_id=mid)匹配到该会员的所有订单。至于status = request.GET.get('status', '') if status != ' ': olist = olist.filter(status=status)这三段代码,和前端页面紧密相连,当前端点击某状态时,后端 经过处理筛选出该状态下的订单。olist.order_by("-id")的功能是根据id号对订单进行降序出路。
每一种语言的基础学好都非常重要,像这次,列表运用的非常精妙。order_status = ["无","排队中","已撤销","已完成"]定义了一个名叫order_status的列表,列表中有“无”、“排队中”、"已撤销"、“已完成”四个元素,vo.statusinfo = order_status[vo.status]中vo.status是在数据库中获取订单的状态,数据库中的订单状态是以数字的形式存放的,现在将vo.status作为索引放到order_status中,打个比方,当前端的用户点击"排队中"时,“排队中"对应的status=1,后端先利用olist = olist.filter(status=status)将排队中的订单筛选出来,现在订单状态都是"排队中",这次再通过vo.statusinfo = order_status[vo.status] 将排队中的订单以文字形式展示出来。既然vo.statusinfo = order_status[vo.status] 是将数据渲染到前端,那么前端对这些数据有用,等说到前端了继续讲解。
因为每个订单订餐数量大于等于1,若将每个订单的订餐数量展示出来,如果某个订单订餐数量多,可能页面空间不足。怎么办呢?展示每个订单的前4条餐品详情信息。将功能逻辑以代码形式展现
for vo in list2:
plist = OrderDetail.objects.filter(order_id=vo.id)[:4] # 获取前4条
vo.plist = plist
后端说完了,咱们再梳理下前端。在后端代码梳理中咱们说了会根据前端对订单状态的点击而有对应订单逻辑处理。那么前端怎么点击订单状态,订单状态又是以什么前端代码形式展现的呢?看下面代码
<!--选项卡-->
<nav class="memberOrder-nav line">
<a href="{% url 'mobile_member_orders' %}" class="line select">全部</a>
<a href="{% url 'mobile_member_orders' %}?status=1" class="line">排队中</a>
<a href="{% url 'mobile_member_orders' %}?status=3">已完成</a>
</nav>
<div class="memberOrder-header"></div>
默认的是全部,status关键字不会传到后端,如果点击排队中或已完成,status=1或status=3会将信息传到后端,后端进行逻辑判断。前端的展示页面如下
订单状态说完了,接着说下前端怎么接受后端渲染过来的数据 {% for order in orderslist %}
<div class="memberOrder-list line" onclick="location.href='{% url 'mobile_member_detail' order.id %}' ">
<p><b>订单编号:</b>{{ order.id }}<span>({{ order.statusinfo }})</span></p>
<div class="order-product line">
<ul>
<li>
{% for vo in order.plist %}
<img src="{% static 'uploads/product/' %}{{ vo.product.cover_pic }}" alt="">
{% endfor %}
</li>
</ul>
</div>
<p>
<b>总价:</b><span>¥{{ order.money }}</span>元
<b>下单时间:</b>{{ order.create_at|date:'Y-m-d H:i' }}
</p>
</div>
{% endfor %}
看上面的代码,首先遍历后端传过来的数据orderslist,onclick="location.href='{% url 'mobile_member_detail' order.id %}' 的意思点击某个订单会跳到该订单的详情页,这个后面会详细讲。这里可以将{% url 'mobile_member_detail' order.id %}去掉,不然运行时会报错。前端页面要显示订单编号同时还要显示订单状态,这些直接在后端打包传过来的数据中拿就可以了。 <p><b>订单编号:</b>{{ order.id }}<span>({{ order.statusinfo }})</span></p>中的order.statusinfo是不是很熟悉呀,没错,就是在后端运用列表等基本的语法规则将数据库中的status改为了文字形式。
遍历了订单还不够,因为一个订单中会有多个餐品,咱们还需要将餐品详情也都遍历出来,循环控制语句解决了这个问题。在订单页,这次设计的只是展示每个订单餐品的图片,在数据库中获取到图片的名字,然后在对应的目录下将图片取出来就可以了,代码: <img src="{% static 'uploads/product/' %}{{ vo.product.cover_pic }}" alt="">
订单总价和下单时间在数据库中都有,直接取出来就可以。"我的订单"列表页是不是很简单呢!
继续往下写,在订单列表,点击某一订单能跳到订单详情页就好了。这个功能可以实现,来梳理下逻辑。
def detail(request,uid):
'''浏览会员订单详情'''
order = Orders.objects.get(id=uid)
order_status = ["无","排队中","已撤销","已完成"]
#获取关联其他表数据(订单详情,店铺信息)
plist = OrderDetail.objects.filter(order_id=order.id)
order.plist = plist
shop = Shop.objects.only("name").get(id=order.shop_id)
order.shopname = shop.name
order.statusinfo = order_status[order.status] # 转换订单状态
return render(request, "mobile/member-detail.html", {"order":order})
以上是订单详情页的后端代码逻辑。当用户点击某一订单时,会获取到该订单的id号,通过前端传过来的id号获取该订单的信息,然后通过订单id号匹配到对应的订单详情数据,通过订单表中的商铺id获取对应的商铺名称,并将订单详情数据、商铺名称都打包到order中。订单状态这块的逻辑是不是很熟悉,和刚才订单列表页的订单状态逻辑一样。
说完了后端,现在开始看下前端的实现。
{% extends 'mobile/base.html' %}
{% load static %}
{% block mainbody %}
<body ontouchstart="return true;">
<!--头部-->
<header>
<div class="header">
<h1>订单详情</h1>
<a href="{% url 'mobile_member_orders' %}" class="return"><i></i></a>
</div>
</header>
<!--内容区-->
<article class="main-container">
<!--订单编号-->
<div class="memberDetailheader line">订单号:{{ order.id }}{{ order.statusinfo }}</div>
<!--购买商品-->
<div class="list-content cartlist order-Pro memberDetaillist">
<ul class="line-li">
{% for vo in order.plist %}
<li>
<a href="#"><div class="pro-img">
<img src="{% static 'uploads/product/' %}{{ vo.product.cover_pic }}" alt=""></div></a>
<div class="pro-con"><h3>{{ vo.product_name }}</h3><b>{{ vo.price }}元</b><p>X{{ vo.quantity }}</p></div>
</li>
{% endfor %}
</ul>
</div>
<!--价格、下单时间-->
<div class="memberDetailPrice line">
<p>共计:{{ order.plist|length }} 个 金额:{{ order.money }}元</p>
<p style="font-size: 13px;">
下单时间:{{ order.create_at|date:'Y-m-d H:i' }}<br/>
{{ order.shopname }}
</p>
</div>
</article>
</body>
{% endblock %}
前端主要是将后端的数据再遍历出来,订单详情页的代码逻辑和订单列表的逻辑差不多,这里不做过多详细解释。order.plist|length这块特别说明下,后端里order.plist是对应的该订单下订单详情里的订餐数据,length指的是订单详情表里有多少个该订单对应的餐品,是做统计用的。
前面已经说了个人中心模块的订单列表和订单详情页,接下来说一下一个非常简单的逻辑:退出。现在想一下,何为退出呢?就是将缓存的用户信息清除,跳到登录页面呗。对,就是这样做的。这块逻辑主要是在后端,后端的实现也非常简单。
def logout(request):
'''执行会员退出'''
del request.session['mobileuser']
return render(request, 'mobile/register.html')
这里注意下,清除要用del。我在调试这段代码时用了request.session['mobileuser'] = {},结果没有达到预期结果。现在复盘,应该是将该用户缓存信息置为空,说明该用户缓存信息只是为空了,还是存在该用户信息的,所以出现了一系列bug。而将用户缓存信息del,说明该用户的信息已经被清除了,缓存信息没有了,页面自然跳转到登录页面。
现在个人中心还有我的资料、修改密码、收货地址等功能需要实现,因为时间关系就不一一实现了。等有时间了我再去一一实现这些功能,有兴趣的同学可以尝试实现下,下一章节开始讲大堂点餐功能
,