查询购物车数据
1. 后端接口设计
请求方式 : GET /cart/
请求参数: 无
返回数据: JSON 或 表单
[
{
"id": 9,
"count": 3,
"name": "华为 HUAWEI P10 Plus 6GB+64GB 钻雕金 移动联通电信4G手机 双卡双待",
"default_image_url": "http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrRcUeAHp9pAARfIK95am88523545",
"price": "3388.00",
"selected": true
},
{
"id": 12,
"count": 1,
"name": "华为 HUAWEI P10 Plus 6GB+64GB 钻雕蓝 移动联通电信4G手机 双卡双待",
"default_image_url": "http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrRdICAO_CRAAcPaeOqMpA2024091",
"price": "3388.00",
"selected": true
}
]
参数 | 类型 | 是否必须 | 说明 |
---|---|---|---|
id | int | 是 | 商品sku id |
count | int | 是 | 数量 |
selected | bool | 是 | 是否勾选,默认勾选 |
name | str | 是 | 商品名称 |
default_image_url | str | 是 | 商品默认图片 |
price | decimal | 是 | 商品单价 |
2. 后端实现
在carts/serializers.py中创建序列化器
class CartSKUSerializer(serializers.ModelSerializer):
"""
购物车商品数据序列化器
"""
count = serializers.IntegerField(label='数量')
selected = serializers.BooleanField(label='是否勾选')
class Meta:
model = SKU
fields = ('id', 'count', 'name', 'default_image_url', 'price', 'selected')
在carts/views.py 中修改视图,增加get方法
class CartView(APIView):
...
def get(self, request):
"""
获取用户的购物车记录
"""
# 1. 获取user
try:
user = request.user
except Exception:
user = None
# 2. 获取用户的购物车记录
if user:
# 2.1 如果用户已登录,从redis中获取用户的购物车记录
redis_conn = get_redis_connection('cart')
# 获取用户购物车中商品的id和数目
# {
# '<sku_id>': '<count>',
# ...
# }
cart_key = 'cart_%s' % user.id
redis_cart = redis_conn.hgetall(cart_key)
# 获取购物车被选中的商品的id
cart_selected_key = 'cart_selected_%s' % user.id
redis_cart_selected = redis_conn.smembers(cart_selected_key)
# 组织数据
# {
# '<sku_id>': {
# 'count': '<count>',
# 'selected': '<selected>'
# },
# ...
# }
cart_dict = {}
for sku_id, count in redis_cart.items():
cart_dict[int(sku_id)] = {
'count': int(count),
'selected': sku_id in redis_cart_selected
}
else:
# 2.2 如果用户未登录,从cookie中获取用户的购物车记录
cookie_cart = request.COOKIES.get('cart')
if cookie_cart:
cart_dict = pickle.loads(base64.b64decode(cookie_cart))
else:
cart_dict = {}
# 3. 根据购物车记录获取对应商品的信息
skus = SKU.objects.filter(id__in=cart_dict.keys())
for sku in skus:
sku.count = cart_dict[sku.id]['count']
sku.selected = cart_dict[sku.id]['selected']
# 4. 将商品数据序列化并进行返回
serializer = CartSKUSerializer(skus, many=True)
return Response(serializer.data)
3. 前端实现
修改cart.html文件,增加Vue变量
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>美多商城-购物车</title>
<link rel="stylesheet" type="text/css" href="css/reset.css">
<link rel="stylesheet" type="text/css" href="css/main.css">
<script type="text/javascript" src="js/host.js"></script>
<script type="text/javascript" src="js/vue-2.5.16.js"></script>
<script type="text/javascript" src="js/axios-0.18.0.min.js"></script>
</head>
<body>
<div id="app">
<div class="header_con">
<div class="header">
<div class="welcome fl">欢迎来到美多商城!</div>
<div class="fr">
<div v-if="username" class="login_btn fl">
欢迎您:<em>{{ username }}</em>
<span>|</span>
<a @click="logout">退出</a>
</div>
<div v-else class="login_btn fl">
<a href="login.html">登录</a>
<span>|</span>
<a href="register.html">注册</a>
</div>
<div class="user_link fl">
<span>|</span>
<a href="user_center_info.html">用户中心</a>
<span>|</span>
<a href="cart.html">我的购物车</a>
<span>|</span>
<a href="user_center_order.html">我的订单</a>
</div>
</div>
</div>
</div>
<div class="search_bar clearfix">
<a href="index.html" class="logo fl"><img src="images/logo.png"></a>
<div class="sub_page_name fl">| 购物车</div>
<form method="get" action="/search.html" class="search_con fr mt40">
<input type="text" class="input_text fl" name="q" placeholder="搜索商品">
<input type="submit" class="input_btn fr" name="" value="搜索">
</form>
</div>
<div class="total_count">全部商品<em>{{total_count}}</em>件</div>
<ul class="cart_list_th clearfix">
<li class="col01">商品名称</li>
<li class="col03">商品价格</li>
<li class="col04">数量</li>
<li class="col05">小计</li>
<li class="col06">操作</li>
</ul>
<ul class="cart_list_td clearfix" v-for="(sku,index) in cart">
<li class="col01"><input type="checkbox" name="" v-model="sku.selected" @change="update_selected(index)"></li>
<li class="col02"><img :src="sku.default_image_url"></li>
<li class="col03">{{ sku.name }}</li>
<li class="col05">{{ sku.price }}元</li>
<li class="col06">
<div class="num_add">
<a @click="on_add(index)" class="add fl">+</a>
<input v-model="sku.count" @focus="origin_input=sku.count" @blur="on_input(index)" type="text" class="num_show fl">
<a @click="on_minus(index)" class="minus fl">-</a>
</div>
</li>
<li class="col07">{{sku.amount}}元</li>
<li class="col08"><a @click="on_delete(index)">删除</a></li>
</ul>
<ul class="settlements">
<li class="col01"><input type="checkbox" name="" @change="on_selected_all" v-model="selected_all"></li>
<li class="col02">全选</li>
<li class="col03">合计(不含运费):<span>¥</span><em>{{total_selected_amount}}</em><br>共计<b>{{total_selected_count}}</b>件商品</li>
<li class="col04"><a href="place_order.html">去结算</a></li>
</ul>
<div class="footer">
<div class="foot_link">
<a href="#">关于我们</a>
<span>|</span>
<a href="#">联系我们</a>
<span>|</span>
<a href="#">招聘人才</a>
<span>|</span>
<a href="#">友情链接</a>
</div>
<p>CopyRight © 2016 北京美多商业股份有限公司 All Rights Reserved</p>
<p>电话:010-****888 京ICP备*******8号</p>
</div>
</div>
<script type="text/javascript" src="/js/cart.js"></script>
</body>
</html>
新建cart.js文件
var vm = new Vue({
el: '#app',
data: {
host,
username: sessionStorage.username || localStorage.username,
user_id: sessionStorage.user_id || localStorage.user_id,
token: sessionStorage.token || localStorage.token,
cart: [],
total_selected_count: 0,
origin_input: 0 // 用于记录手动输入前的值
},
computed: {
total_count: function(){
var total = 0;
for(var i=0; i<this.cart.length; i++){
total += parseInt(this.cart[i].count);
this.cart[i].amount = (parseFloat(this.cart[i].price) * parseFloat(this.cart[i].count)).toFixed(2);
}
return total;
},
total_selected_amount: function(){
var total = 0;
this.total_selected_count = 0;
for(var i=0; i<this.cart.length; i++){
if(this.cart[i].selected) {
total += (parseFloat(this.cart[i].price) * parseFloat(this.cart[i].count));
this.total_selected_count += parseInt(this.cart[i].count);
}
}
return total.toFixed(2);
},
selected_all: function(){
var selected=true;
for(var i=0; i<this.cart.length; i++){
if(this.cart[i].selected==false){
selected=false;
break;
}
}
return selected;
}
},
mounted: function(){
// 获取购物车数据
axios.get(this.host+'/cart/', {
headers: {
'Authorization': 'JWT ' + this.token
},
responseType: 'json',
withCredentials: true
})
.then(response => {
this.cart = response.data;
for(var i=0; i<this.cart.length; i++){
this.cart[i].amount = (parseFloat(this.cart[i].price) * this.cart[i].count).toFixed(2);
}
})
.catch(error => {
console.log(error.response.data);
})
},
methods: {
// 退出
logout: function(){
sessionStorage.clear();
localStorage.clear();
location.href = '/login.html';
},
// 减少操作
on_minus: function(index){
if (this.cart[index].count > 1) {
var count = this.cart[index].count - 1;
this.update_count(index, count);
}
},
on_add: function(index){
var count = this.cart[index].count + 1;
this.update_count(index, count);
},
on_selected_all: function(){
var selected = !this.selected_all;
for (var i=0; i<this.cart.length;i++){
this.cart[i].selected = selected;
this.update_count(i, this.cart[i].count);
}
},
// 删除购物车数据
on_delete: function(index){
},
on_input: function(index){
var val = parseInt(this.cart[index].count);
if (isNaN(val) || val <= 0) {
this.cart[index].count = this.origin_input;
} else {
// 更新购物车数据
}
},
// 更新购物车数据
update_count: function(index, count){
},
// 更新购物车数据
update_selected: function(index) {
}
}
});