P72 商品详情页js代码
购物车模块
1.添加商品到购物车
在详情页面上有一个加入商品到购物车,处理增加与减少部分的js,点击事件
在商品的详情页detail.html中去定义js
# 计算商品的总价格
<script type="text/javascript">
# 进行调用
update_goods_amount()
# 定义点击事件
function update_goods_amount() {
# 获取商品的单价和数量
# 计算商品的总价
# 设置商品的总价
}
# 增加和减少还需要处理
# 增加商品的数量
# 减少商品的数量
# 手动输入商品的数量
</script>
# 获取商品的单价和数量
页面过来之后需要计算一下总价格,价格×数量即为总价格
# show_pirze是它的class,em是它的子标签,标签中的内容即为单价
price = $('.show_pirze').children('em').text()
# num_show是它的class,value值为它的数量
count = $('.num_show').val()
此时拿到的price和count都是字符串,要进行数字转化
# price是小数
price = parseFloat(price)
# count是整数
count= parseInt(count)
# 此时计算总价
amount = price * count
# 设置商品的总价格
# 总价这里有个类class为total
# 在设置的时候需要注意:整数和小数相乘之后,小数点的位数可能不是两位
# 设置的时候需要将其设置为两位的小数,amount.toFixed(2)将小数转换成字符串,并且将小数保留几位数字,比如这里保留两位,则是2,最后加上元
$('.total').children('em').text(amount.toFixed(2) + '元')
##################################
要先获取原有的数目,加1之后,再做设置
$('.add').click(function() {
# 获取商品原有的数目
count = $('.num_show').val()
# 点击增加之后,该值count值加1
count = parseInt(count)+1
# 重新设置商品的数目
$('.num_show').val(count)
# 此时总价也要变,更新商品的总价
update_goods_amount()
})
##################################
要先获取原有的数目,加1之后,再做设置
$('.minus').click(function() {
# 获取商品原有的数目
count = $('.num_show').val()
# 点击增加之后,该值count值加1
count = parseInt(count)-1
# 不能减少到负数,这里需要加一步判断
if (count <= 0) {
count = 1
}
# 重新设置商品的数目
$('.num_show').val(count)
# 此时总价也要变,更新商品的总价
update_goods_amount()
})
##################################
# 手动输入商品的数量
blur()在失去焦点的时候执行一个方法
$('.num_show').blur(function (){
# 获取用户输入的数量
#this即为输入框
count = $(this).val()
# 校验count是否合法,三种不合法的情况
# case1: 如果返回的不是数字则isNaN(count)会返回真
# case2: 有可能输入了一堆空格,把获取到的内容去除两边的空格,看它的长度是多少
# js中去除空格的函数:count.trim(),取其长度count.trim().length
# case3: 有可能输入小于等于0的数字
if (isNaN(count) || count.trim().length == 0 || parseInt(count) <= 0){
count = 1
}
# 重新设置商品的数目
# 处理小数的情况,使用parseInt强制转换成整数
$(this).val(parseInt(count))
# 更新商品的总价
update_goods_amount()
})
P73 购物车记录添加后台view
选择完商品之后,点击“加入购物车”,页面不跳转,需要请求地址
添加商品到购物车:
点击“加入购物车”,详情页不动,此时如何将请求发出去?
1、请求方式,采用ajax(ajax发起请求的时候要返回什么数据?json)
传参方式:
get传参数:/cart/add?sku_id=1&count=3
post传参数:{'sku_id': 1, 'count': 3}
url传参数:URL配置时捕获参数
如何选择传参方式:
如果涉及到数据的修改(新增,更新,删除),采用post
若干只涉及到数据的获取,采用get
2、添加商品到购物车,需要发哪些请求参数过去?
传递参数包括:商品id(sku_id)、商品数量(count)
cart/views.py
from django.views.generic import View
from django-redis import get_redis_connection
# 地址: /cart/add
class CartAddView(View):
"""购物车记录添加"""
def post(self, request):
"""购物车记录添加"""
pass
urls.py
path('add', CartAddView.as_view(), name='add'),
# 地址: /cart/add
class CartAddView(View):
"""购物车记录添加"""
def post(self, request):
"""购物车记录添加"""
user = request.user
if not user.is_authenticated():
# 用户未登录
return JsonResponse({'res': 0, 'errmsg': '请先登录'})
# 接收数据
sku_id = request.POST.get('sku_id')
count = request.POST.get('count')
# 数据校验
if not all([sku_id, count]):
# 'res': 0代表数据不完整
return JsonResponse({'res': 1, 'errmsg': '数据不完整'})
# 校验添加的商品数量
try:
count = int(count)
except Exception as e:
# 说明不是一个有效数字,数目出错
return JsonResponse({'res': 2, 'errmsg': '商品数目出错'})
# 校验商品是否存在
try:
sku = GoodsSKU.objects.get(id=sku_id)
except GoodsSKU.DoesNotExist:
# 商品不存在
return JsonResponse({'res': 3, 'errmsg': '商品不存在'})
# 业务处理: 添加购物车记录
# 添加购物车的几种情况
# 用户的购物车中已经有两条记录'cart_1': {'1': 3, '2': 5}
# 随后用户又往购物车中添加了2件id为1的商品,sku_id=1, count=2,此时添加的时候应该如何处理?
# 查看购物车中有没有该商品,若有则做累加,若没有则添加;
conn = get_redis_connection('default')
cart_key = 'cart_%d' %user.id
#尝试获取sku_id的值 从一个hash中获取一个属性的值 ---》 hget cart_key 属性
# redis.StrictRedis中找到一个方法 hget
# 如果sku_id在hash中不存在,hget返回None
cart_count = conn.hget(cart_key, sku_id)
if cart_count:
count += int(cart_count)
# 在添加之前要考虑商品的库存
# 校验商品的库存
if count > sku.stock:
return JsonResponse({'res': 4, 'errmsg': '商品库存不足'})
# 无论购物车中有没有该商品,其最终存入的都是count值
# 设置hash中sku_id对应的值
# hset(name, key, value)<-----传参:hashkey,属性名字,对应的值
# hset--->如果商品sku_id不存在则进行添加操作,如果存在则进行更新操作
conn.hset(cart_key, sku_id, count)
# 获取用户购物车中的条目数
# 返回应答
return JsonResponse({'res': 5, 'meeage': '添加成功'})
用户未登录的情况下无法添加购物车
P74 购物车记录添加前端js
此时添加购物车视图已经完成,接下来写前端代码,发起ajax post请求,将数据传过来
在detail.html中,
# 需要获取sku_id,则动态地添加一个属性sku_id="{{ sku.id }}"
<a href="javascript:;" class="add_cart" sku_id="{{ sku.id }}" id="add_cart">加入购物车</a>
$(".add_cart").click(function(){
# 获取商品id和商品数量
sku_id = $(this).attr('sku_id')
count = $('.num_show').val()
# alert测试
# alert(sku_id + ':' + count)
# 在发起请求之前组织字典参数
params = {'sku_id': sku_id, 'count': count}
# 发起ajax post请求,访问/cart/add, 传递参数: sku_id count
# function (data) 对应的回调函数,data是后台返回的json数据
$.post('/cart/add', params, function (data){
if (data.res == 5){
// 添加成功
$(".add_jump").css({'left':$add_y+80,'top':$add_x+10,'display':'block'})
$(".add_jump").stop().animate({
'left': $to_y+7,
'top': $to_x+7},
"fast", function() {
$(".add_jump").fadeOut('fast',function(){
// 重新设置用户购物车中商品的条目数,即购物车中的商品总个数
$('#show_count').html(data.cart_count);
});
});
}
else{
// 添加失败
alert(data.errmsg)
}
})
})
进行测试,网页中检查-->Network
# 报错403
1.在加购物车之前添加{% csrf_token %}
2.在ajax post中获取参数时,获取到csrfmiddlewaretoken的值【可在加了csrf_token标签之后通过查看页面源码获取】
csrf = $('input[name="csrfmiddlewaretoken"]').val()
3.组织参数中加入该键值对
params = {'sku_id': sku_id, 'count': count, 'csrfmiddlewaretoken': csrf}
###############################################django防止CSRF的方式:
1. 默认打开csrf中间件
2. 表单post提交数据时加上{% csrf_token %}标签
防御原理:
1. 渲染模板文件时在页面生成一个名字叫做csrfmiddlewaretoken的隐藏域
2. 服务器交给浏览器保存一个名字为csrftoken的cookie信息
3. 提交表单时,两个值都会发给服务器,服务器进行比对,如果一样,则csrf验证通过,否则失败
##############################################
加购完之后,我的购物车应该显示相应的条目数
在views.py中
# 获取用户购物车中的条目数
cart_count = conn.hlen(cart_key)
# 返回应答
return JsonResponse({'res': 5, 'cart_count': cart_count, 'message': '添加成功'})
在post请求中:
$(".add_jump").fadeOut('fast',function(){
// 重新设置用户购物车中商品的条目数,即购物车中的商品总个数
$('#show_count').html(data.cart_count);
});
// 获取add_cart a标签左上角的坐标【加入购物车】
var $add_x = $('#add_cart').offset().top;
var $add_y = $('#add_cart').offset().left;
// 获取show_count div元素左上角的坐标【我的购物车】
var $to_x = $('#show_count').offset().top;
var $to_y = $('#show_count').offset().left;
添加成功之后做了一个动画add_jump
P75 购物车记录添加_小结
至此,购物车添加完成,在处理添加的时候,如果用户未登录则无法进行添加操作,这里为什么没有继承utils下的mixin.py中的LoginRequiredMixin?
ajax发起的请求都在后台,在浏览器中看不到效果,即使浏览器访问了登录页面,也无法感知,因此需要自己进行判断并返回相应的json
前后端分离开发时:
确定前端是否传递数据,传递什么数据,什么格式
确定前端访问的方式(get, post)
确定返回给前端的什么数据,什么格式
P76 购物车页面显示
127.0.0.1:8000/static/cart.html
购物车页面展示
需要用户登陆后才能进行展示,继承LoginRequiredMixin
from utils.mixin import LoginRequiredMixin
class CartInfoView(LoginRequiredMixin, View):
"""购物车页面显示"""
def get(self, request):
"""显示"""
# 获取登录的用户
user = request.user
# 获取用户购物车中商品的信息 (在redis中存储)
conn = get_redis_connection('default')
cart_key = 'cart_%s' %(user.id)
# 购物车记录的格式是
'cart_用户id':{'sku_id1': 商品数目, 'sku_id2': 商品数目}
要获取其中所有的数据:hgetall(name)<----根据名字来获取key,返回一个Python的dict
# {'商品id': 商品数量}
cart_dict = conn.hgetall(cart_key)
# 并用一个列表接收查询到的信息
skus = []
# 保存用户购物车中商品的总数目和总价格
total_count = 0 # 总件数
total_price = 0 # 总价格
# 页面要获取每个商品的信息
# 可以拿到商品id获取到商品信息
# 遍历获取商品的信息,可以通过遍历字典每个元素cart_dict.items()<----键和值
for sku_id, count in cart_dict.items():
# 根据商品的id获取商品的信息
sku = GoodsSKU.objects.get(id=sku_id)
# 计算商品的小计 商品有了,则很容易获取到价格,将计算得到的值传给模板
amount = sku.price * int(count)
# amount与一个商品对象关联,考虑商品首页中动态的给一个对象增加属性,参考type
# 动态给SKU对象增加一个属性amount,保存商品的小计
sku.amount = amount
# 用户购物车中的数量也要获取,并传给模板
# 动态给SKU对象增加一个属性amount,保存购物车中对应商品的数量
sku.count = count
# 添加到列表
skus.append(sku)
# 需要计算购物车中的商品总件数和总价格 总件数和条目数不一样
# 累加计算商品的总数目和总价格
total_count += int(count)
total_price += amount
# 组织上下文
context = {
'total_count': total_count,
'total_count': total_count,
'skus': skus
}
# 使用模板
return render(request, 'cart.html', context)
re_path(r'^$', CartInfoView.as_view(), name='show') # 购物车页面显示
购物车信息的获取
1.购物车中商品的数量
2.购物车中每个商品的信息
3.总计以及商品的总数量
P77 购物车js-全选-全不选-商品的选中和不选中
jQuery的选择器找checkbox
input标签是ul的子集元素(ul的孙子。。。)
$('.settlements').find(':checkbox')<---全选的checkbox
选择器:所有被选中的input元素$(':checked')
# 获取所有被选中的商品的checkbox
$('.cart_list_td').find(':checked')
目的:获取商品的ul,以此获取其中的小计和数量,从而进行累加计算
所有被选中的商品的checkbox有个父级元素ul
parent和parents两者区别:parent是找它老爸,parents是它老爸及其以上
# 获取所有被选中的商品所在的ul元素,此时获取到的ul元素对应的商品一定是被选中的
$('.cart_list_td').find(':checked').parents('ul')
cart.html下方添加
1.定义函数一:计算被选中的商品的总件数和总价格
2.定义函数二:商品的全选和全不选,调用函数一获取选中的商品的总件数和总价格,更新页面的信息
3.定义函数三:商品对应的checkbox状态发生改变时,设置全选checkbox的状态,即当任一商品的不被勾选时,全选随之取消。
不管是哪个商品的checkbox,只要一改变都需要去判断一下全选是否要被选中,因此需要去绑定每个商品的checkbox的绑定事件,只要一改变就需要去做
{% block bottomfiles %}
<script src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script>
// 计算被选中的商品的总件数和总价格
function update_page_info() {
# 分别保存商品的总件数和总价格
total_count = 0
total_price = 0
// $('.cart_list_td')《---选中和没选中的都找到了
# 获取所有被选中的商品所在的ul元素,此时获取到的ul元素对应的商品一定是被选中的
# each()进行遍历,循环遍历之后可以获取到商品的总件数和总价格
$('.cart_list_td').find(':checked').parents('ul').each(function () {
# $(this)对应一个商品的ul元素
# 计算商品的总件数和总价格,则需要获取商品的数目和小结
# $(this)对应一个商品的ul元素,要获取到ul中对应的商品数目在num_show里面【input元素,其class为num_show,其value值是商品的数目】
count = $(this).find('.num_show').val()
# 获取商品的小计,在li元素里面,li是ul的儿子,因此可以使用children(),根据类来找
amount = $(this).find('.col07').text()
# 获取完之后需要进行累加,求得总和
# 累加计算商品的总件数和总价格,将count累加到total_count上【注:count和amount是字符串,需要进行转换】
count = parseInt(count)
amount = parseFloat(amount)
total_count += count
total_price += amount
})
// 设置被选中的商品的总件数和总价格
// 在em中设置总价格,在b元素中设置商品的总件数,即找到settlements【ul元素,其类为settlements】
// 计算之后,小数点个数可能不是两位,使用toFixed(2)进行设置
$(.'settlements').find('em').text(total_price.toFixed(2))
$(.'settlements').find('b').text(total_count)
}
//商品的全选和全不选
// 当其进行改变的时候执行方法change
$('.settlements').find(':checkbox').change(function () {
// 获取全选的checkbox的选中状态,即获取checkbox的属性
is_checked = $(this).prop('checked') // 返回True或者False
// 遍历商品对应的checkbox,设置这些checkbox的选中状态和全选的checkbox保持一致
// 每个商品都在一个ul中,先找到对应的ul,再找其中的checkbox each()实现遍历
$('.cart_list_td').find(':checkbox').each(function () {
//每个$(this)就是checkbox,prop设置获取到的属性与全选的选中状态is_checked保持一致
//此时可以实现商品的选中,全选和全不选
$(this).prop('checked', is_checked)
// 总计和商品件数也要随之改变,计算被选中的商品的总件数和总价格
})
// 更新页面的信息
update_page_info()
})
// 商品对应的checkbox状态发生改变时,设置全选checkbox的状态
// 在function中判断全选是否应该被选中
// 思路一:找到购物车页面中一共有几个checkbox,再找到一共有几个被选中,被选中的数目小于所有checkbox的总和,则说明有商品未被选中,此时【全选】被选中,否则不被选中
$('.cart_list_td').find(':checkbox').change(function () {
// 获取页面上所有商品的数目
// 分析,每个商品都在一个ul中,class为cart_list_td
// 找到cart_list_td对应的ul,length:找到几个ul就有几件商品
all_len = $('.cart_list_td').length
// 获取页面上被选中的商品的数目
checked_len = $('.cart_list_td').find(':checked').length
is_checked = true
if (checked_len < all_len){
is_checked = false
}
$('.settlements').find(':checkbox').prop('checked', is_checked)
/*
if (checked_len < all_len) {
$('.settlements').find(':checkbox').prop('checked', false)
}
else{
$('.settlements').find(':checkbox').prop('checked', true)
}*/
// 更新页面的信息
update_page_info()
})
</script>
{% endblock bottomfiles %}
P78 购物车记录更新-后台view
此时的全选、全不选未涉及到与后台的交互,而加数量和减数量以及手动输入数量则涉及到与后台的交互,该部分实际上都是在更新用户购物车中商品的数量
cart的views.py中
# 更新商品的数量
# 页面发起加数量或减数量后,页面不进行跳转,因此前端采用ajax发起post请求
# 前端要给后端传参数:商品id(sku_id) 更新的商品数量(count)【购物车中存储的内容】
# /cart/update
class CartUpdateView(View):
"""购物车记录更新"""
def post(self, request):
"""购物车记录更新"""
# 在接收数据之前进行用户登录校验
user = request.user
if not user.is_authenticated():
# 用户未登录
return JsonResponse({'res': 0, 'errmsg': '请先登录'})
# 接收数据
sku_id = request.POST.get('sku_id')
count = request.POST.get('count')
# 数据校验
if not all([sku_id, count]):
# 'res': 0代表数据不完整
return JsonResponse({'res': 1, 'errmsg': '数据不完整'})
# 校验添加的商品数量
try:
count = int(count)
except Exception as e:
# 说明不是一个有效数字,数目出错
return JsonResponse({'res': 2, 'errmsg': '商品数目出错'})
# 校验商品是否存在
try:
sku = GoodsSKU.objects.get(id=sku_id)
except GoodsSKU.DoesNotExist:
# 商品不存在
return JsonResponse({'res': 3, 'errmsg': '商品不存在'})
# 业务处理:更新购物车记录
conn = get_redis_connection('default')
cart_key = 'cart_%d' %(user.id)
# 校验商品的库存
if count > sku.stock:
return JsonResponse({'res': 4, 'errmsg': '商品库存不足'})
# 更新
conn.hset(cart_key, sku_id, count)
# 返回应答
return JsonResponse({'res': 5, 'errmsg': '更新成功'})
path('update', CartUpdateView.as_view(), name='update')
P79 购物车记录更新-前端数目增加js
点击加号绑定增加按钮的点击事件----一点击就会发送一个请求
$('.add').click()<---找到增加的a标签并绑定点击事件click
cart.html中
{% block bottomfiles %}
<script src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
<script>
...
// 定义函数 计算商品的小计
// 需要计算哪个小计,则把商品所在的ul元素传过来就可以,此时就可以在里面获取商品的价格和数量
function update_goods_amount(sku_ul) {
// 获取商品的价格和数量
// 将$(this)改为sku_ul即可,你获取到的是哪个商品,需要把外面的元素传过来,就可以拿到它的数量
count = sku_ul.find(".num_show").val()
// 价格在其子元素中的col05,此时可以使用children
price = sku_ul.children('.col05').text()
// 计算商品的小计
amount = parseInt(count)*parseFloat(price)
// 设置商品的小计
sku_ul.children('.col07').text(amount.toFixed(2)+'元')
}
// 购物车商品数量的增加 点击之后需要执行function方法
// 在view中分析可知,前端需要传给后端商品id和商品数量
$('.add').click(function () {
// 获取商品的id和商品的数量,分析页面可知
// $(this)为点击的a标签,该标签下面的input标签中的value值即为商品数量
count = $(this).next().val()
// 往input标签中添加自定义属性sku_id = "{{ sku.id }}"
sku_id = $(this).next().attr('sku_id')
// 此时已经获取到商品id和商品数量,发起的是post请求,要记得csrf验证
// 在<ul class="settlements"></ul>中加上{% csrf_token %}
// 在发参数之前,先获取到csrf
csrf = $('input[name="csrfmiddlewaretoken"]').val()
// 在发送请求之前 组织参数
// 原来的数目是count,点击加号,则是在原来的基础上加一
count = parseInt(count) + 1
params = {
'sku_id': sku_id,
'count': count,
'csrfmiddlewaretoken': csrf
}
// 发起ajax post请求,访问/cart/update, 传递参数:sku_id count
// 在function函数中会返回一个数据,在这里面可以做判断
// 在更新之前定义一个变量 error_update 如果更新成功则该值为False,如果更新失败则该值为True
error_update = false
// *step4a的获取页面上购物车商品的总件数<---更新成功后
total = 0
$.post('/cart/update', params, function (data) {
// 在views中如果成功则res==5
if (data.res == 5){
// 更新成功
error_update = false
// *step4b获取页面上购物车商品的总件数
total = data.total_count
}
else{
// 更新失败 弹出错误信息
error_update = true
alert(data.errmsg)
}
// 经过更新之后就可以判断更新是否成功
if (error_update == false) {
// 成功的话:
// 1.需要重新设置商品的数目,即数字要加一,
// 2.小计也要改变
// 3. 获取商品对应的checkbox的选中状态,如果被选中,更新页面信息
// 1.重新设置商品的数目,需要设置为count+1的值
$(this).next().val(count)
// 2.计算商品的小计 可以单独写一个函数计算商品的小计 调用该函数即可
// $(this)是a标签,ul是a标签的父元素,这里sku_ul=$(this).parents('ul')
update_goods_amount($(this).parents('ul'))
// 3. 获取商品对应的checkbox的选中状态,如果被选中,更新页面信息
// 获取其父级中的ul,找到ul中的checkbox,找到其属性的值prop('checked')
is_checked = $(this).parents('ul').find(':checkbox').prop('checked')
// 如果is_checked成立,则更新页面信息
if (is_checked){
// 更新页面信息
// 即计算被选中商品的总件数以及总价格 只需要调用update_page_info即可实现更新
update_page_info()
}
// 截止目前位置的效果已经满足上述三种要求,当未选中但是加减商品时,页面左上角的总件数【全部商品xx件】需要改变
// 4.更新页面上购物车商品的总件数《---去后台views中的更新后面进行计算
// *step4c设置页面上购物车商品的总件数 要设置的地方在【全部商品】em标签中的内容
$('.total_count').children('em').text(total)
}
})
})
views.py
...
# 更新
# 计算用户购物车中商品的总件数,此时并不是条目数,而是商品的总件数{'1': 5, '2': 3},其中条目数为2,总件数为8
# 需要获取cart_key中的所有的value值获取到hash中有一个value值 取一个hash中的所有的value值
# hvals(name) <---哈希中的所有值作为列表返回
# 调用conn的方法拿到vals
total_count = 0
vals = conn.hvals(cart_key)
for val in vals:
total_count += int(val)
# 返回应答 放入json中传给前端
return JsonResponse({'res': 5, 'total_count': total_count, 'errmsg': '更新成功'})
# 去前端js代码中,如何获取到total_count
再进行测试的时候增加完后为0,原因:
首先在*step4a中定义total为0,要在*step4c中设置值,需要在回调函数$.post('/cart/update'...)执行完成之后,才会给total赋值,即*step4b,
现在直接是0则说明没有等待回调执行。
默认发起的ajax请求都是异步的,不会等回调函数执行,因此最终在 *step4c设置页面上购物车商品的总件数时,会设置为0
这里在发起请求之前设置ajax请求为同步
$.ajaxSettings.async = false<---之后发起的ajax请求则为同步的,这个设置会影响全局的,因此在发完请求之后在将其设置为true
$.post('/cart/update', params, function (data){...}
$.ajaxSettings.async = true
在这样设置之后,ajax请求发出之后,回调完成之前,代码会一直阻塞在这里,最终就可以获取到该值
除了total需要发起一个同步的请求之外,还有其他地方也需要,这里的error_update也需要同步。
如果是异步的话,error_update值永远为初值,未获取到更新后的真实值,而其后面的操作都是依赖于这个请求的更新结果的
P80 购物车记录更新-前端数目减少-手动输入js
购物车商品数量的减少
增加与减少同理,这里绑定的类是minus
获取商品的数量
增加是在输入框的前面,而减少是在输入框的后面,把其中所有的next()改成prev()
count = parseInt(count) - 1
如果减小到0直接return
if (count <= 0){
return
}
其余操作相同
这里可以把增加购物车数量和减少购物车数量抽象出来:更新购物车中商品的数量
function update_remote_cart_info(sku_id, count) {
// 获取到csrf
// 发起请求之前组织参数
}
// 获得焦点事件
// 保存用户输入之前购物车中商品的数目
var pre_count = 0
$('.num_show').focus(function () {
pre_count = $(this).val()
})
手动输入修改
手动输入的参数可能是非法的,这里需要绑定失去焦点事件
输入框的class是num_show
$('.num_show').blur(function () {...})
其中的$(this)就是input框
// 获取商品的id和商品的数目
var count = $(this).val()
var sku_id = $(this).attr('sku_id')
// 判断用户输入的商品数目是否合法
if (isNaN(count) || count.trim().length==0 || parseInt(count)<=0){
// 设置商品的数目为用户输入之前的数目
// 一进来需要先记录原来的值,输入的内容不合法就不做改变,直接返回原来的值
$(this).val(pre_count)
return
}
// 输入值count通过了上述三种校验情况,可能是一个数字,因此在更新之前将count转化为数字
// 更新购物车记录
count = parseInt(count)
update_remote_cart_info(sku_id, count)
// 设置更新
if (error_update == false){
// 重新设置商品的数目
$(this).val(count)
// 更新商品的小计
update_sku_amount($(this).parents('ul'))
// 判断是否更新选中商品的总件数和总金额
is_checked = $(this).parents('ul').find(':checkbox').prop('checked')
if (is_checked){
// 更新页面信息
update_page_info()
}
// 更新页面购物车商品的总件数
$('.total_count').children('em').text(total_count)
}
else{
// 如果更新失败了,例如超过商品的库存
// 设置商品的数目为用户输入之前的数目
$(this).val(pre_count)
}
关于购物车数量的更新---增加,减少和手动输入,三者处理过程类似,都是通过ajax post请求访问/cart/update,区别在于一些参数校验和获取值的时候不同
P81 购物车记录删除-后台view
购物车页面上每个商品后面都有一个删除,这部分涉及到购物车记录的删除
# 删除购物车记录
# 采用ajax post请求
# 前端需要传递的参数: 商品id(sku_id)
# /cart/delete
class CartDeleteView(View):
"""购物车记录删除"""
def post(self, request):
user = request.user
if not user.is_authenticated:
return JsonResponse({'res': 0, 'errmsg': '请先登录'})
# 接收参数
sku_id = request.POST.get('sku_id')
# 数据校验
if not sku_id:
return JsonResponse({'res': 1, 'errmsg': '无效的商品id'})
# 校验商品是否存在
try:
sku = GoodsSKU.objects.get(id=sku_id)
except GoodsSKU.DoesNotExist:
return JsonResponse({'res': 2, 'errmsg': '商品不存在'})
# 业务处理: 删除购物车记录
conn = get_redis_connection('default')
cart_key = 'cart_%d' % user.id
# 删除 hdel
conn.hdel(cart_key, sku_id)
# 计算用户购物车中商品的总件数{'1':5,'2':3}
total_count = 0
vals = conn.hvals(cart_key)
for val in vals:
total_count += int(val)
return JsonResponse({'res': 3, 'total_count': total_count, 'message': '删除成功'})
url中配置
path('delete', CartDeleteView.as_view(), name='delete')
P82 购物车记录删除-前端js
获取删除的点击事件,绑定a标签的点击事件,执行对应的方法
$('.cart_list_td').children('col08').children('a').click(function () {
// 前端需要传给后端商品id
// 这里则需要在前端去获取对应商品id
// $(this)<---a标签,a标签的父元素ul中的input标签的自定义属性sku_id
sku_id = $(this).parents('ul').find('num_show').attr('sku_id')
// 删除之前拿出csrf
// 在发参数之前,先获取到csrf
csrf = $('input[name="csrfmiddlewaretoken"]').val()
// 在发送请求之前 组织参数
params = {
'sku_id': sku_id,
'csrfmiddlewaretoken': csrf
}
// 获取商品所在的ul元素
sku_ul = $(this).parents('ul')
// 发起ajax post请求,访问/cart/delete, 传递参数:sku_id 执行回调函数
//
$.post('/cart/delete', params, function (data) {
// 回调函数,成功之后返回的是3
// 所以这里去判断,如果data.res返回的是3则说明删除成功
if (data.res == 3) {
// 删除成功,成功之后要移除页面上商品所在的ul元素
// 如果直接在这里删除的话,这里的$(this)就不是删除的a标签了,这里在外面上面获取一下其ul标签
// sku_ul.remove()<--- remove()方法是移除自身及其子元素
// sku_ul.empty()<--- remove()方法是移除其子元素不包括自身
// 这里是需要移除自身及其子元素,使用remove方法
sku_ul.remove()
// 移除之后,其合计要根据选中或未选中进行判断
// 获取sku_ul中商品的选中状态
is_checked = sku_ul.find(':checkbox').prop('checked')
// is_checked为真表示被选中了,则页面中的合计需要更新
if (is_checked) {
// 更新页面信息
update_page_info()
}
// 左上角的【全部商品】,不管有没有选中都需要进行更新《---需要在views中获取并传过来
// 重新设置页面上购物车中商品的总件数
$('.total_count').children('em').text(data.total_count)
}
else{
// 失败则直接弹出错误信息
alert(data.errmsg)
}
})
})
views.py
# 计算用户购物车中商品的总件数{'1':5,'2':3}
total_count = 0
vals = conn.hvals(cart_key)
for val in vals:
total_count += int(val)
return JsonResponse({'res': 3, 'total_count': total_count, 'message': '删除成功'})
P83 购物车模块-小结
第一部分:
计算被选中的商品的总件数和总价格
计算商品的小计
商品的全选和全不选
商品对应的checkbox状态发生改变时,设置全选checkbox的状态
以上js不涉及和后台的交互
第二部分:
涉及到后台交互,购物车商品数量的更新
在该部分分析了
前端的请求方式,应该传过来的参数
与更新视图相关的js《---添加,减少,手动输入
第三部分:
购物车记录的删除