zoukankan      html  css  js  c++  java
  • DJANGO-天天生鲜项目从0到1-010-购物车-购物车操作页面(勾选+删改)

    购物车页面展示

    分析购物车页面需要后台查询的数据有哪些,最基础的是购物车中goods对象,还有一些比如购物车中每个商品的小计金额、所有商品的总数目以及总价。虽然这些信息通过前台js代码也可以进行计算展示,但是最好是后台查询数据时一并计算一起发给前端,减轻前端的计算压力。

    class CartView(LoginRequiredMixin, View):
        '''购物车视图类'''
        template_name = 'cart/cart.html'
        def get(self, request):
            '''显示购物车'''
            user = request.user
            # 获取购物车信息
            connect = get_redis_connection('default')
            cart_key = 'cart_%d'%(user.id)
            cart = connect.hgetall(cart_key)
            # 返回给前端的goods_list
            goods_list = []
            total_count = 0
            total_amount = 0
            for goods_id, count in cart.items():
                goods = Goods.objects.get(id=int(goods_id))
                # 小计
                amount = goods.price * int(count)
                # 给goods对象新增属性
                goods.amount = amount
                goods.count = int(count)
                # 将商品添加至列表中
                goods_list.append(goods)
                # 合计
                total_count += int(count)
                total_amount += amount
            # 组织上下文
            context = {
                'goods_list': goods_list,
                'total_count': total_count,
                'total_amount': total_amount,
            }
            return render(request, self.template_name, context)
    对应HTML模板:
    {% extends 'base_no_cart.html' %}
    {% load static %}
    {% block title %}天天生鲜-购物车{% endblock title %}
    {% block infoname %}购物车{% endblock infoname %}
    {% block body %}
    <div class="total_count">全部商品<em>{{ total_count }}</em></div>
    <ul class="cart_list_th clearfix">
        <li class="col01">商品名称</li>
        <li class="col02">商品单位</li>
        <li class="col03">商品价格</li>
        <li class="col04">数量</li>
        <li class="col05">小计</li>
        <li class="col06">操作</li>
    </ul>
    <form method="post" action="{% url 'order:place' %}">
        {% csrf_token %}
        {% for goods in goods_list %}
        <ul class="cart_list_td clearfix">
            <li class="col01"><input type="checkbox" name="goods_ids" value="{{ goods.id }}" checked></li>
            <li class="col02"><a href="{% url 'goods:detail' goods.id %}"><img src="{{ goods.image.url }}"></a></li>
            <li class="col03"><a href="{% url 'goods:detail' goods.id %}">{{ goods.name }}<br><em>{{ goods.price }}元/{{ goods.uom }}</em></a></li>
            <li class="col04">{{ goods.uom }}</li>
            <li class="col05">{{ goods.price }}元</li>
            <li class="col06">
                <div class="num_add">
                    {% csrf_token %}
                    <a href="javascript:;" class="add fl">+</a>
                    <input type="text" goods_id="{{ goods.id }}" class="num_show fl" value="{{ goods.count }}">
                    <a href="javascript:;" class="minus fl">-</a>
                </div>
            </li>
            <li class="col07">{{ goods.amount }}元</li>
            <li class="col08"><a href="javascript:;" class="delete">删除</a></li>
        </ul>
        {% endfor %}
        <ul class="settlements">
            <li class="col01"><input type="checkbox" name="" checked=""></li>
            <li class="col02">全选</li>
            <li class="col03">合计(不含运费):<span>¥</span><em>{{ total_amount }}</em><br>共计<b>{{ total_count }}</b>件商品</li>
            <li class="col04"><input type="submit" value='去结算'/></li>
        </ul>
    </form>
    {{ errmsg }}
    {% endblock body %}
    {% block endfiles %}
    <script src="{% static 'js/jquery-1.12.4.min.js'%}"></script>
    <script>
        //全选按钮
        $('.settlements').find(':checkbox').change(function(){
            //获取全选checkbox的全选状态
            is_checked = $(this).prop('checked')
            //遍历设置商品的checkbox
            $('.cart_list_td').find(':checkbox').each(function(){
                $(this).prop('checked', is_checked)
            })
            //刷新总价格和总数量
            update_page_info()
        })
    
        //单个CheckBox监听
        $('.cart_list_td').find(':checkbox').change(function(){
            //获取全部商品的数目
            all_count = $('.cart_list_td').length
            //获取被选中商品的数目
            checked_count = $('.cart_list_td').find(':checked').length
            //判断两者数目是否相等,不相等则设置全选为未选中,否则设置为选中
            $('.settlements').find(':checkbox').prop('checked', all_count == checked_count)
            //更新总数量和价格
            update_page_info()
        })
    
        //加号点击事件
        $('.add').click(function(){
            count = update_count($(this).next(), 1)
            goods_id = $(this).next().attr('goods_id')
            //发送ajax请求
            send_ajax_change(goods_id, count)
        })
    
        //减号点击事件
        $('.minus').click(function(){
            count = update_count($(this).prev(), -1)
            goods_id = $(this).prev().attr('goods_id')
            //发送ajax请求
            send_ajax_change(goods_id, count)   
        })
    
        //手动修改数量
        $('.num_show').blur(function(){
            //获取数量
            count = $(this).val()
            if(isNaN(count) || count.trim().length==0 || parseInt(count) <=0){
                count = 1
                $(this).val(parseInt(count))
            }    
            //提交ajax请求
            goods_id = $(this).attr('goods_id')
            count = parseInt(count)
            send_ajax_change(goods_id, count)
        })
    
        //删除按钮
        $('.delete').click(function(){
            //获取商品所在的ul元素
            goods_ul = $(this).parents('.cart_list_td')
            //获取商品id
            goods_id = goods_ul.find('.num_show').attr('goods_id')
            send_ajax_delete(goods_id)
            //移除界面数据
            goods_ul.remove()
            //刷新总数量和总价格
            update_page_info()
        })
    
        //数量加减
        function update_count(num_show, num){
            //获取原数量
            count = parseInt(num_show.val())
            //计算新数量
            count += num
            if (count <= 0){
                count = 1
            }
            //重新设置数量
            num_show.val(count)
            return count
        }
    
        //更新选中商品的总价格和总数量
        function update_page_info(){
            var total_amount = 0
            var total_count = 0
            $('.cart_list_td').find(':checked').parents('ul').each(function(){
                count = parseInt($(this).find('.num_show').val())
                amount = parseFloat($(this).find('.col07').text())
                total_count += count
                total_amount += amount
            })
            //更新总件数和价格
            $('.settlements').find('em').text(total_amount.toFixed(2))
            $('.settlements').find('b').text(total_count)
        }
    
        //发送ajax请求,修改购物车信息
        function send_ajax_change(goods_id, count){
            csrf = $('input[name="csrfmiddlewaretoken"]').val()
            parameter = {
                'goods_id': goods_id,
                'count': count,
                'csrfmiddlewaretoken': csrf
            }
            $.post('change/', parameter, function(data){
                //回调函数
                if(data.status == 'S'){
                    //刷新全部商品件数
                    $('.total_count').children('em').text(data.total_count)
                    //刷新小计,找到传进来的goods_id对应的那一行input,找到其父节点下的小计节点
                    amount = parseInt(data.amount)
                    $('input[goods_id='+parameter.goods_id+']').parents('.cart_list_td').children('.col07').text(amount.toFixed(2)+'')//刷新总数量和总价格
                    update_page_info()
                }
                else{
                    alert(data.errmsg)
                    //将数量重置为改变前
                    $('input[goods_id='+parameter.goods_id+']').val(data.count)
                }
            })
        }
    
        //发送ajax请求,删除商品
        function send_ajax_delete(goods_id){
            csrf = $('input[name="csrfmiddlewaretoken"]').val()
            parameter = {
                'goods_id': goods_id,
                'csrfmiddlewaretoken': csrf
            }
            $.post('delete/', parameter, function(data){
                if(data.status == 'S'){
                    //刷新总数量
                    total_count = parseInt(data.total_count)
                    $('.total_count').children('em').text(total_count)
                }else{
                    alert(data.errmsg)
                }
            })
        }
    </script>
    {% endblock endfiles%}

    全选框和勾选框

    以下Js代码知识点基于jquery

    1. 获取子标签可通过find和children,对应的获取父标签为parents和parent:

      .find('selector'),遍历当前元素集合中每个元素的后代。不管是多少代后代。如find('元素名')、find('#id')、find('.class')、find(':type')等

      .children(selector),遍历当前元素集合中的第一代后代,只有儿子辈。如children('元素名')、children('#id')、children('.class')、children(':type')等

    2. 勾选框的勾选和取消动作对应的是change方法

    3. 获取标签的属性可通过attr 与 prop获取:

      在jQuery中attr表示HTML文档节点属性,而prop则表示JS对象属性。

      例如,下面这个 HTML 元素:<input type="text" value="Name:"> 拥有两个 attributes。

      一旦浏览器解析该代码,HTMLInputElement 对象就会被创建,并且该对象会拥有很多 properties,如:accept、accessKey、align、alt、attributes、autofocus、baseURI、checked、childElementCount、ChildNodes、children、classList、className、clientHeight 等等。

      对于某个 DOM 节点对象,properties 是该对象的所有属性,而 attributes 是该对象对应元素(标签)的属性。当一个 DOM 节点为某个 HTML 元素所创建时,其大多数 properties 与对应 attributes 拥有相同或相近的名字。

      但是对于表单元素的checkedselecteddisabled等属性,attribute的checked、selected、disabled就是表示该属性初始状态的值,property的checked、selected、disabled才表示该属性实时状态的值(值为true或false)。因此得使用prop()函数来设置或获取checkedselecteddisabled等属性

        //更新选中商品的总价格和总数量
        function update_page_info(){
            var total_amount = 0
            var total_count = 0
            $('.cart_list_td').find(':checked').parents('ul').each(function(){
                count = parseInt($(this).find('.num_show').val())
                amount = parseFloat($(this).find('.col07').text())
                total_count += count
                total_amount += amount
            })
            //更新总件数和价格
            $('.settlements').find('em').text(total_amount.toFixed(2))
            $('.settlements').find('b').text(total_count)
        }
        //全选按钮
        $('.settlements').find(':checkbox').change(function(){
            //获取全选checkbox的全选状态
            is_checked = $(this).prop('checked')
            //遍历设置商品的checkbox
            $('.cart_list_td').find(':checkbox').each(function(){
                $(this).prop('checked', is_checked)
            })
            //刷新总价格和总数量
            update_page_info()
        })
        //单个CheckBox监听
        $('.cart_list_td').find(':checkbox').change(function(){
            //获取全部商品的数目
            all_count = $('.cart_list_td').length
            //获取被选中商品的数目
            checked_count = $('.cart_list_td').find(':checked').length
            //判断两者数目是否相等,不相等则设置全选为未选中,否则设置为选中
            $('.settlements').find(':checkbox').prop('checked', all_count == checked_count)
            //更新总数量和价格
            update_page_info()
        })

    实时修改购物车中商品数量

    以下Js代码知识点基于jquery:

    1. .next()获取下一标签,.prev()获取上一标签

    2. input框的失去焦点时会调用触发.blur()方法

    3. 校验数量时:

      isNaN(x):如果 x 是特殊的非数字值 NaN(或者能被转换为这样的值),返回的值就是 true。如果 x 是正常的数字,则返回 false。NaN,该值表示一个非法的数字

      .trim().length:去掉空格后的长度

      parseInt(string, radix):将字符串解析为数字,只有字符串中的第一个数字组合会被返回。parseInt("10"); //返回 10  parseInt("10.56"); //返回 10

    4. 在django中提交ajax的post请求时,也需要经过django自带的CSRF认证,可以在html模板代码中随意位置(习惯写在触发ajax请求的按钮或者链接上)加上{% CSRF_TOKEN%},加上了之后,浏览器将其解析hidden的input代码:

    <input type="hidden" name="csrfmiddlewaretoken" value="9BdYDF3097s9UGgYJ6L8aGmqkL9DJCgMZehs5xT4Ebdrlvdg74KMXuEvcQpksO6C">

    通过

    csrf = $('input[name="csrfmiddlewaretoken"]').val()

    获取值,并在post参数中加入 'csrfmiddlewaretoken': csrf,注意这里的key值'csrfmiddlewaretoken'名字固定不能更改

    5. toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。

        //加号点击事件
        $('.add').click(function(){
            count = update_count($(this).next(), 1)
            goods_id = $(this).next().attr('goods_id')
            //发送ajax请求
            send_ajax_change(goods_id, count)
        })
    
        //减号点击事件
        $('.minus').click(function(){
            count = update_count($(this).prev(), -1)
            goods_id = $(this).prev().attr('goods_id')
            //发送ajax请求
            send_ajax_change(goods_id, count)   
        })
        //手动修改数量
        $('.num_show').blur(function(){
            //获取数量
            count = $(this).val()
            if(isNaN(count) || count.trim().length==0 || parseInt(count) <=0){
                count = 1
                $(this).val(parseInt(count))
            }    
            //提交ajax请求
            goods_id = $(this).attr('goods_id')
            count = parseInt(count)
            send_ajax_change(goods_id, count)
        })
        //数量加减
        function update_count(num_show, num){
            //获取原数量
            count = parseInt(num_show.val())
            //计算新数量
            count += num
            if (count <= 0){
                count = 1
            }
            //重新设置数量
            num_show.val(count)
            return count
        }
        //发送ajax请求,修改购物车信息
        function send_ajax_change(goods_id, count){
            csrf = $('input[name="csrfmiddlewaretoken"]').val()
            parameter = {
                'goods_id': goods_id,
                'count': count,
                'csrfmiddlewaretoken': csrf
            }
            $.post('change/', parameter, function(data){
                //回调函数
                if(data.status == 'S'){
                    //刷新全部商品件数
                    $('.total_count').children('em').text(data.total_count)
                    //刷新小计,找到传进来的goods_id对应的那一行input,找到其父节点下的小计节点
                    amount = parseInt(data.amount)
                    $('input[goods_id='+parameter.goods_id+']').parents('.cart_list_td').children('.col07').text(amount.toFixed(2)+'元')//刷新总数量和总价格
                    update_page_info()
                }
                else{
                    alert(data.errmsg)
                    //将数量重置为改变前
                    $('input[goods_id='+parameter.goods_id+']').val(data.count)
                }
            })
        }

    新增CartChangeView变更购物车商品数量

    class CartChangeView(View):
        '''购物车改变视图'''
        def post(self, request):
            user = request.user
            context = {
                'status': 'E',
                'errmsg': ''
            }
            # 判断是否登录
            if not user.is_authenticated:
                context['errmsg'] = '用户未登录!'
                return JsonResponse(context)
    
            # 获取数据
            goods_id = request.POST.get('goods_id')
            count = request.POST.get('count')
    
            # 校验数据
            if not all([goods_id, count]):
                context['errmsg'] = '数据不完整!'
                return JsonResponse(context)
            # 商品是否存在
            try:
                goods = Goods.objects.get(id=int(goods_id))
            except Goods.DoesNotExist:
                context['errmsg'] = '商品不存在!'
                return JsonResponse(context)
            # 数量格式是否正确
            try:
                count = int(count)
            except Exception as e:
                context['errmsg'] = '数量格式不正确!'
                return JsonResponse(context)
    
            # 更新redis数据库
            connect = get_redis_connection('default')
            cart_key = 'cart_%d'%(user.id)
            # 校验库存
            if count > goods.onhand:
                context['errmsg'] = '库存不足!'
                # 返回原数量
                context['count'] = int(connect.hget(cart_key, goods_id))
                return JsonResponse(context)
    
            # 更新值
            connect.hset(cart_key, goods_id, count)
    
            # 重新计算总件数
            total_count = 0
            values = connect.hvals(cart_key)
            for val in values:
                total_count += int(val)
            # 上下文
            context['amount'] = goods.price * count
            context['total_count'] = total_count
            context['status'] = 'S'
            return JsonResponse(context)

    删除购物车中商品

    以下Js代码知识点基于jquery:

    1. .remove() 方法删除被选元素及其子元素。

        //删除按钮
        $('.delete').click(function(){
            //获取商品所在的ul元素
            goods_ul = $(this).parents('.cart_list_td')
            //获取商品id
            goods_id = goods_ul.find('.num_show').attr('goods_id')
            send_ajax_delete(goods_id)
            //移除界面数据
            goods_ul.remove()
            //刷新总数量和总价格
            update_page_info()
        })
        //发送ajax请求,删除商品
        function send_ajax_delete(goods_id){
            csrf = $('input[name="csrfmiddlewaretoken"]').val()
            parameter = {
                'goods_id': goods_id,
                'csrfmiddlewaretoken': csrf
            }
            $.post('delete/', parameter, function(data){
                if(data.status == 'S'){
                    //刷新总数量
                    total_count = parseInt(data.total_count)
                    $('.total_count').children('em').text(total_count)
                }else{
                    alert(data.errmsg)
                }
            })
        }

    新增CartDeleteView删除购物车商品

    class CartDeleteView(View):
        '''购物车删除视图'''
        def post(self, request):
            user = request.user
            context = {
                'status': 'E',
                'errmsg': ''
            }
            if not user.is_authenticated:
                context['errmsg'] = '用户未登录!'
                return JsonResponse(context)
            # 接收数据
            goods_id = request.POST.get('goods_id')
    
            # 校验数据
            try:
                goods_id = int(goods_id)
                goods = Goods.objects.get(id=goods_id)
            except Exception as e:
                context['errmsg'] = '商品不存在'
                return JsonResponse(context)
    
            # 删除购物车数据
            connect = get_redis_connection('default')
            cart_key = 'cart_%d'%(user.id)
            connect.hdel(cart_key, goods_id)
    
            # 重新获取商品数量
            vals = connect.hvals(cart_key)
            total_count = 0
            for val in vals:
                total_count += int(val)
    
            # 上下文
            context['total_count'] = total_count
            context['status'] = 'S'
            return JsonResponse(context)
  • 相关阅读:
    C# winfrom容器布局与工具栏&&右键菜单栏&&隐藏显示小图标的的简单事件
    C# Winform ListView控件
    MongoDB3.6.3 windows安装配置、启动
    史蒂夫•乔布斯在斯坦福大学的演讲
    SpringBoot配置文件 application.properties详解
    Elasticsearch分布式安装启动失败
    Couldn't connect to host, port: smtp.163.com, 25; timeout -1;
    CentOS 7 安装jdk9
    生成唯一的随机数(时间+随机数)
    idea 设置背景图片
  • 原文地址:https://www.cnblogs.com/gcxblogs/p/12889206.html
Copyright © 2011-2022 走看看