总计价格计算

计算购物车中被选中的商品的价格采取:后端必须算,前端选算。

用一个函数计算商品总价格
views_helper:

def get_total_price():
    # 找到被选中的商品
    carts = Cart.objects.filter(c_is_select=True)
    
    total = 0
    
    for cart in carts:
        total += cart.c_goods_num*cart.c_goods.price
    
    return total

views:导入上述函数

def cart(request):

    carts = Cart.objects.filter(c_user=request.user)
    # 判断是不是全部被选中,全部被选中就返回True
    is_all_select = not carts.filter(c_is_select=False).exists()

    data = {
        'title': '购物车',
        'carts': carts,
        'is_all_select': is_all_select,
        'total_price': get_total_price(),
    }

    return render(request, 'main/cart.html', context=data)

接下来来到cart.html中将价格传入页面:
cart:

                <p>
                    <span>全选</span>
                    <span>共计:</span>
                    <span id="total_price">{{ total_price }}</span>
                </p>

下面我们要将计算总价的函数放入所有需要返回价格的地方:
views:

def allselect(request):

    cart_list = request.GET.get('cart_list')

    cart_list = cart_list.split('#')

    carts = Cart.objects.filter(id__in=cart_list)  # 这样查询数据库的次数变少

    for cart_obj in carts:
        cart_obj.c_is_select = not cart_obj.c_is_select
        cart_obj.save()

    data = {
        'status': 200,
        'msg': 'ok',
        'total_price': get_total_price(),
    }
    return JsonResponse(data=data)

def subshopping(request):
    # 接受cartid
    cartid = request.GET.get('cartid')
    # 从数据库中查找到对应的
    cart_obj = Cart.objects.get(pk=cartid)

    data = {
        'status': 200,
        'msg': 'ok',

    }
    # 判断数据库中商品数量
    if cart_obj.c_goods_num > 1:  # 如果数量大于1就减少1
        cart_obj.c_goods_num = cart_obj.c_goods_num - 1
        cart_obj.save()
        data['c_goods_num'] = cart_obj.c_goods_num
    else:  # 如果数量小于等于1就从数据库中删除
        cart_obj.delete()
        data['c_goods_num'] = 0
    data['total_price'] = get_total_price()
    return JsonResponse(data=data)

def change_cart_state(request):
    # 从ajax获得商品id,但是这里有个隐藏漏洞,这里没有验证这个cart_id是用户已经存在的
    cart_id = request.GET.get('cartid')
    # 去数据库中匹配对应的商品id
    cart_obj = Cart.objects.get(pk=cart_id)
    # 将原本选中的状态取反
    cart_obj.c_is_select = not cart_obj.c_is_select
    # 记得保存,如果不保存,数据库的状态不会改变
    cart_obj.save()
    # 判断这个商品是否被选中
    is_all_select = not Cart.objects.filter(c_user=request.user).filter(c_is_select=False).exists()
    data = {
        'status': 200,
        'msg': 'change ok',
        'c_is_select': cart_obj.c_is_select,
        'is_all_select': is_all_select,
        'total_price': get_total_price(),
    }
    return JsonResponse(data=data)

前端价格联动

修改完商品数量之后,需要实时在客户的界面更新价格。
cart.js:

$(function(){
    $('.confirm').click(function(){
        console.log('change state');
        /*修改选中状态*/
        var $confirm = $(this);

        var $li = $confirm.parents('li');

        var cartid = $li.attr('cartid');
        /*利用ajax,找到对应标签之后发送给服务器修改状态*/
        $.getJSON('/peiqi1/changecartstate/',{'cartid': cartid}, function (data){
            console.log(data);

            if (data['status']===200){
                $('#total_price').html(data['total_price']); //实时更新价格
                if(data['c_is_select']){
                    $confirm.find('span').find('span').html('√');
                }else{
                    $confirm.find('span').find('span').html('');
                }
                if (data['is_all_select']){
                    $('.all_select span span').html('√');
                }else{
                    $('.all_select span span').html('');

                }
            }
        })
    })
    $('.subShopping').click(function (){

        var $sub = $(this);

        var $li = $sub.parents('li');

        var cartid = $li.attr('cartid');

        $.getJSON('/peiqi1/subshopping/', {'cartid':cartid}, function(data){
            console.log(data);
            /*把修改之后的数量传回用户所在的前端页面*/

            if (data['status']===200){
                $('#total_price').html(data['total_price']); //实时更新价格
                if (data['c_goods_num']> 0){
                    var $span = $sub.next('span');
                    $span.html(data['c_goods_num']);
                }else{
                    $li.remove();
                }
            }
        })

    })
    $('.all_select').click(function(){

        var $all_select = $(this);
        // 点击全选按钮,如果有未选中就变成选中的,如果是全选中的就变成所有的都未选中
        var select_list = [];
        var unselect_list = [];

        $('.confirm').each(function(){
            var $confirm = $(this);

            var cartid = $confirm.parents('li').attr('cartid');

            if($confirm.find('span').find('span').html().trim()){
                //将选中的加入选中的表
                select_list.push(cartid);
            }else{  //将未选中的加入未选中表
                unselect_list.push(cartid);
            }
        })  //遍历
        //当没有选中的列表大于0,就将unselect_list发送给服务器改为选中
        if(unselect_list.length>0){
            $.getJSON('/peiqi1/allselect', {'cart_list': unselect_list.join('#')}, function (data){
                console.log(data);
                if(data['status']===200){
                    $('.confirm').find('span').find('span').html('√');  //按下全选,将所有的按钮都改为选中
                    $all_select.find('span').find('span').html('√');
                    $('#total_price').html(data['total_price']); //实时更新价格
                }
            })
        }else{
            if (select_list.length>0){
                $.getJSON('/peiqi1/allselect', {'cart_list': select_list.join('#')}, function (data){
                console.log(data);
                if(data['status']===200){
                    $('.confirm').find('span').find('span').html('');  //取消全选,将所有选中改为未选中
                    $all_select.find('span').find('span').html('');
                    $('#total_price').html(data['total_price']); //实时更新价格
                }
            })
            }
        }
    })
})

在200状态之后都要计算一次总价,所以在其后加上 $('#total_price').html(data['total_price']);

下单功能

点击下单,将我们选中的商品从购物车表中移除,并且生成订单表。

我们将下单按钮换成span按钮,采用ajax去完成  

<span id="make_order">下单</span>

然后我们需要去用js找到这个标签:
cart.js:

    $('#make_order').click(function (){
        //判断选中还是未选中
        var select_list = [];
        var unselect_list = [];

        $('.confirm').each(function(){
            var $confirm = $(this);

            var cartid = $confirm.parents('li').attr('cartid');

            if($confirm.find('span').find('span').html().trim()){
                //将选中的加入选中的表
                select_list.push(cartid);
            }else{  //将未选中的加入未选中表
                unselect_list.push(cartid);
            }
        })  //遍历
        
        if(select_list.length===0){
            return //如果下单时没有选中商品就删除
        }
        // 购物车商品不为空,就把数据发送给服务端
        $.getJSON('/peiqi1/makeorder/', function(data){
            console.log(data);
        })

然后我们需要写url接口:
urls新建:

url(r'^makeorder/', views.make_order, name='make_order'),

将该接口加入中间件中:

REQUIRE_LOGIN_JSON = [
    '/peiqi1/addtocart/',
    '/peiqi1/changecartstate/',
    '/peiqi1/makeorder/',
]

在views_contant中添加订单状态简称:

# 已下单未付款
ORDER_STATUS_NOT_PAY = 1
# 已下单已付款未发货
ORDER_STATUS_NOT_SEND = 2
# 已下单已付款已发货未收货
ORDER_STATUS_NOT_RECEIVE = 3
# 已下单已付款已发货已收货未确认
# 已下单已付款已发货已收货已确认
# 已下单已付款已发货已收货已确认未评论
# 已下单已付款已发货已收货已确认已评论未追评

# 已下单已付款已发货已收货已确认已评论
    # 申请售后
        # 退货
        # 换货
        # 返修

创建订单models:

class Order(models.Model):
    o_user = models.ForeignKey(peiqi1user)  # 用户
    o_price = models.FloatField(default=0)  # 总价
    o_time = models.DateTimeField(auto_now=True)  # 订单创建日期
    o_status = models.IntegerField(default=ORDER_STATUS_NOT_PAY)
    
    class Meta:
        db_table = 'peiqi1_order'


class OrderGoods(models.Model):
    o_order = models.ForeignKey(Order)
    o_goods = models.ForeignKey(Goods)
    o_goods_num = models.IntegerField(default=1)
    class Meta:
        db_table = 'peiqi1_ordergoods'

然后迁移数据库

views:

def make_order(request):
    # 先将所有商品取出来
    carts = Cart.objects.filter(c_user=request.user).filter(c_is_select=True)
    # 将商品放入订单表中
    order = Order()
    order.o_user = request.user
    order.o_price = get_total_price()
    order.save()
    
    # 商品级联
    for cart_obj in carts:
        ordergoods = OrderGoods()
        ordergoods.o_order = order
        ordergoods.o_goods_num = cart_obj.c_goods_num
        ordergoods.o_goods = cart_obj.c_goods
        ordergoods.save()
        cart_obj.delete()
    
    data = {
        'status': 200,
        'msg': 'ok',
        'order_id': order.id
    }

    return JsonResponse(data)

测试能否生成订单号:

观察数据库order表



订单详情页面

订单生成之后用户界面需要跳转到订单详情页面。

cart.js:

        $.getJSON('/peiqi1/makeorder/', function(data){
            console.log(data);
            
            if(data['status']===200){
                window.open('/peiqi1/orderdetail/?orderid=' + data['oder_id'], target='_self');
            }
        })

接下来新建url:

url(r'^orderdetail/', views.order_detail, name='order_detail'),

因为订单详情需要验证用户登录,将url添加到中间件中:

REQUIRE_LOGIN = [
    '/peiqi1/cart/',
    '/peiqi1/orderdetail',
]

接着新建模板文件夹order,以及base_orderdetail.html,和orderdetail.html

base_orderdetail.html:

{% extends 'base.html' %}

orderdetail.html:

{% extends 'base_order.html' %}

views:

def order_detail(request):
    # 获取orderid
    order_id = request.GET.get('orderid')
    order = Order.objects.get(pk=order_id)
    data = {
        'title': '订单详情',
        'order': order,
        
    }
    return render(request, 'order/order_detail.html')

现在我们完成了骨架的架构,下面编写订单详情页面

新建order静态资源文件夹及其css子文件夹,并且新建order_detail.css:

/

order_detail.css:

/**************menu***************/
.menuList {
    border-bottom: 0.04rem solid lightgrey;
    height: 2.5rem;
    position: relative;
}

.confirm, .all_select{
    padding: 0.95rem 0;
    width: 15%;
    height: 2.5rem;
    display: inline-block;
    box-sizing: border-box;
    text-align: center;
    float: left;
}

.confirm > span, .all_select>span{
    box-sizing: border-box;
    border: 0.04rem solid orange;
    background: white;
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    overflow: hidden;
    border-radius: 50%;
    line-height: 0.6rem;
}

.confirm > span > span, .all_select>span>span {
    background: yellow;
    font-size: 0.5rem;
    display: block;
}

.menuList > a {
    width: 84%;
    display: inline-block;
    font-size: 0.4rem;
    line-height: 1rem;
}

.menuList > a > img {
    margin-top: 0.25rem;
    width: 25%;
    height: 100%;
    float: left;
}

.menuList > a > p {
    width: 70%;
    height: 1rem;
    float: right;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.menuList > section {
    position: absolute;
    right: 0.4rem;
    bottom: 0.4rem;
    height: 0.75rem;
    border-radius: 1rem;
}

.menuList > section > button {
    background: white;
    border: 1px solid orange;
    border-radius: 1111px;
    color: red;
    display: inline-block;
    text-align: center;
    line-height: 0.65rem;
    font-weight: 900;
    width: 0.75rem;
    height: 0.75rem;
}

.menuList > section > span {
    display: inline-block;
    width: 0.5rem;
    text-align: center;
    line-height: 0.5rem;
    font-size: 0.4rem;
}

.presentPrice:before {
    content: "¥";
    font-size: 0.33rem;
}

/*************payTheBill 买单*************/
.payTheBill {
    height: 1.5rem;
    position: relative;
}

.payTheBill .all_select {
    width: 10%;
    padding-top: 0.4rem;
    padding-left: 0.4rem;
}

.payTheBill > p {
    line-height: 1.5rem;
    text-indent: 0.3rem;
}

.payTheBill > p > span:first-child {
    padding-right: 0.8rem;
}

.payTheBill > p > span:last-child {
    padding-left: 0.3rem;
    color: red;
}

.payTheBill > p > span:last-child:before {
    content: "¥";
    font-size: 0.35rem;
}

.payTheBill > span {
    background: yellow;
    position: absolute;
    right: 0;
    line-height: 1.5rem;
    top: 0;
    padding: 0 0.7rem;
}

oder_detail.html:

{% extends 'base_order.html' %}
{% load static %}

{# 导入css #}
{% block ext_css %}
    {{ block.super }}
    <link rel="stylesheet" href="{% static 'peiqi1/order/css/order_detail.css' %}">
{% endblock %}

{% block content %}
    <div id='order_detail' class="container">
        <h6>订单编号:{{ order.id }}</h6>
        {# 取出商品信息 #}

        <ul>
            {% for ordergoods in order.ordergoods_set.all %}
                <li class="menuList">

                    <a href="#">
                        <img src="{{ ordergoods.o_goods.productimg }}" alt="{{ ordergoods.c_goods.productlongname }}">
                        <p>{{ ordergoods.o_goods.productlongname }}</p>
                        <p class="presentPrice">{{ ordergoods.o_goods.price }}</p>
                    </a>

                    <section>
                        <span>{{ ordergoods.o_goods_num }}</span>
                    </section>
                </li>
            {% endfor %}
        </ul>

    <h6 class="total_price">总价:<span id="total_price">{{ order.o_price }}</span></h6>
    <button class="btn btn-success btn-block">支付</button>

    </div>
{% endblock %}

base_order.css:

{% extends 'base.html' %}
{% load static %}
{% block ext_css %}
{{ block.super }}
    <link rel="stylesheet" href="{% static 'peiqi1/order/css/order.css' %}">
{% endblock %}

{% block header %}
<header></header>
{% endblock %}

新建order.css
order.css:

header{
    height: 1.5rem;
    background: #2fea83;
    margin-bottom: 0.3rem;
}

测试:

最后修改:2024 年 03 月 13 日
如果觉得我的文章对你有用,请随意赞赏