实现三:商品和评价切换(续)
food-detail-list的内容:
...
<section class="menu_detail_list" v-for="(foods,foodindex) in item.foods" :key="foodindex">
<!--点击跳转到食物详情页面,需要传入的参数:
image_path:图片路径,
description:食物描述
month_sales:月销量
name:食物名字
ratting:评分,
rating_count:销售量,
satisfy_rate:满意度,
foods:食物,
shopId:店铺id-->
<router-link class="menu_detail_link" :to="{
path:'shop/foodDetail',query:{image_path:foods.image_path,
description:foods.description,
month_sales:foods.month_sales,
name:foods.name,
rating:foods.rating,
rating_count:foods.rating_count,
satisfy_rate:foods.satisfy.rate,
foods.shopId}
}" tag="div">
<!--图片 s-->
<section class="menu_food_img">
<img :src="imgBaseUrl+foods.image_path">
</section>
<!--图片 e-->
<!--描述信息 s-->
<section class="menu_food_description">
<h3 class="food_description_head">
<!--标题-->
<strong class="description_foodname">
{{foods.name}}
</strong>
<!--是否是新品 s-->
<ul class="attributes_ul" v-if="foods.attributes.length">
<li v-if="attribute" v-for="(attribute,foodIndex) in foods.attributes"
:key="foodindex" :style="{color:'#'+attribute.icon_color,
borderColor:'#'+attribute.icon_color}"
:class="{attribute_new:attribute.icon_name=='新'}">
<!--attribute.icon_name=='新'的时候,显示name值,并未其添加样式-->
<p :style="{color:attribute.icon_name=='新'?'#fff':'#'+attribute.icon_color}">
{{attribute.icon_name=='新'?'新品':attribute.icon_name}}
</p>
</li>
</ul>
<!--是否是新品 e-->
</h3>
<!--描述信息-->
<p class="food_description_content">
{{foods.description}}
</p>
<!--销量和满意度-->
<p class="food_description_sale_rating">
<span>月售{{foods.month_sales}}份</span>
<span>好评率{{foods.satisfy_rate}}%</span>
</p>
<!--foods.activity是已售的意思吗-->
<p v-if="foods.activity" class="food_activity">
<span :style="{color:'#'+foods.activity.image_text_color,
borderColor:'#'+foods.activity.icon_color}">
{{foods.activity.image_text}}
</span>
</p>
</section>
<!--描述信息 e-->
</router-link>
<!--价格 s-->
<footer class="menu_detail_footer">
<section class="food_price">
<span>¥</span>
<span>{{foods.specfoods[0].price}}</span>
<span v-if="foods.specifications.length"></span>
</section>
</footer>
<!--价格 e-->
</section>
...
说明:
foods的数据结构如下:
功能(1):增加点击右侧"..."显示说明信息
...
<header class="menu_detail_header">
...
<!--点击部分-->
<span class="menu_detail_header_right" @click="showTitleDetail(index)"></span>
<!--点击的时候控制TitleDetailIndex值进而控制列表的展示和隐藏-->
<p class="description_tip" v-if="index==TitleDetailIndex">
<span>{{item.name}}</span>
{{item.description}}
</p>
</header>
...
JS部分:
export default{
data(){
return{
...
//点击展示列表头部详情
TitleDetailIndex:null
}
},
//说明信息的隐藏和显示
showTitleDetail(index){
if(this.TitleDetailIndex==index){
this.TitleDetailIndex=null;
}
else{
this.TitleDetailIndex=index;
}
}
}
功能(2):加购
若标识规格的参数specifications没有长度的时候显示+号,若标识规格的参数specifications有长度时显示"规格"。
需要用到Vuex。
知识点:
(1)vuex的状态存储是响应式的,所以从store中读取状态最简单的方法就是通过computed计算属性。
每当state中的状态发生变化时,就会重新计算获取计算属性从而映射到View。
(2)当一个组件有多个状态的时候,会造成计算属性重复和代码冗余。为了解决这个问题,我们可以使用
mapState辅助函数帮助我们生成计算属性:
(3)Mutations修改状态的值,vue视图是由数据驱动的,也就是说state里面的数据是动态变化的,而改变的
唯一方法就是mutation,通俗的理解,mutations里面装着一些改变数据方法的集合。每个 mutation 都有一个字符串的事件类型 (type) 和 一个回调函数 (handler)。事件类型就是通过$store.commit()提交的事件。回调
函数就是我们实际进行状态更改的地方,它作为store中的mutations对象的一个属性而存在。
(4)在组件中提交Mutations
首先引入mapMutations
import {mapState,mapMutations} from 'vuex'
添加methods属性,并加入mapMutations
eg:
...
methods:{
//如果组件中事件的名称和mutations中方法的名称相同,可以传一个字符串数组
...mapMutations([
'add'//映射this.add()为this.$store.commit('add')
])
}
(5)Actions:是异步修改状态。actions和mutations是类似的,不同之处在于:
- Action提交的是Mutation,不能够直接修改state中的状态,而Mutations是可以直接修改state中状态的;
- Action是支持异步操作的,而Mutations只能是同步操作。
实现:
mutation-types.js中定义:
//加入购物车
export const ADD_CART = 'ADD_CART'
//移除购物车
export const REDUCE_CART = 'REDUCE_CART'
//清空购物车
export const CLEAR_CART='CLEAR_CART'
//保存商铺ID
export const SAVE_SHOPID = 'SAVE_SHOPID'
mutations.js中定义方法:
//引入常量
import {ADD_CART,REDUCE_CART,CLEAR_CART,SAVE_SHOPID} from './mutation-types.js'
import {setStore,getStore} from '../config/mUtils';
export default{
//加入购物车
[ADD_CART](state,{
//店铺id
shopid,
//分类id
categpry_id,
item_id,
food_id,
name,
price,
//规格
specs,
//打包费
packing_fee,
//身份证
sku_id,
//库存
stock
}){
let cart=state.cartList;
let shop=cart[shopid]= (cart[shopid] || {});
let category=shop[category_id]= (shop[category_id] || {});
let item=category[item_id] = (category[item_id] || {});
if(item[food_id]){
item[food_id]['num']++;
}
else{
item[food_id]={
"num":1,
///食物id
"id":food_id,
//名字
"name":name,
//价格
"price":price,
//规格
"specs":specs,
//打包费
"packing_fee":packing_fee,
//身份证
"sku_id":sku_id,
//库存
"stock":stock
};
}
state.cartList={...cart};
//存入localStorage
setStore('buyCart',state.cartList);
},
//移出购物车
[REDUCE_CART](state,{
shopid,
category_id,
item_id,
food_id,
name,
price,
specs
}){
let cart=state.cartList;
let shop=(cart[shopid] || {});
let category = (shop[category_id] || {});
let item = (category[item_id] || {});
if(item&&item[food_id]){
if(item[food_id]['num']>0){
item[food_id]['num']--;
state.cartList={..cart};
//存入localStorage
setStore('buyCart',state.cartList);
}
else{
//商品数量为0,则清空当前商品的信息
item[food_id]=null;
}
}
},
//清空当前商品的购物车信息
[CLEAR_CART](state,shopid){
state.cartList[shopid]=null;
state.cartList={...state.cartList};
setStore('buyCart',state.cartList);
},
//保存商铺id
[SAVE_SHOPID](state,shopid){
state.shopid=shopid;
}
}
store文件夹下的index.js:
const state={
...
//加入购物车的商品列表
cartList:{},
//商铺id
shopid:null,
//购物车id
cartId:null
}
export default new Vuex.Store({
state,
getters,
actions,
mutations
})
buyCart.vue:
<section class="cart_module">
<!--显示+/-号的情况 s -->
<section class="cart_button" v-if="!foods.specifications.length">
<!--减号(有数字的时候显示)-->
<transition name="showReduce">
<span v-if="foodNum">
<!--点击减号执行removeOutCart,removeOutCart的对应方法
在mutations.js中
需要传入的参数:
category_id(类别id)
item_id(项目id)
specfoods(特殊食品)的:
food_id(食品id),
name(食品的名字),
price(食品的价格),
packing_fee(食品包装费)
sku_id(库存id)
stock(存货)
-->
<svg @click="removeOutCart(
foods.category_id,
foods.item_id,
foods.specfoods[0].food_id,
foods.specfoods[0].name,
foods.specfoods[0].price,
'',
foods.specfoods[0].packing_fee,
foods.specfoods[0].sku_id,
foods.specfoods[0].stock)">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#cart-minus" />
</svg>
</span>
</transition>
<!--数字 foodNum来自于mutations.js中-->
<transition name="fade">
<span class="cart_num" v-if="foodNum">{{foodNum}}</span>
</transition>
<!--加号:点击+号执行addToCart方法,传入参数同上-->
<svg class="add_icon" @click="addToCart(
foods.category_id,
foods.item_id,
foods.specfoods[0].food_id,
foods.specfoods[0].name,
foods.specfoods[0].price,
'',
foods.specfoods[0].packing_fee,
foods.specfoods[0].sku_id,
foods.specfoods[0].stock,
$event)">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#cart-add" />
</svg>
</section>
<!--显示+/-号的情况 e-->
<!--显示规格的情况 s-->
<section class="choose_specification" v-else>
<section class="choose_icon_container">
<transition name="showReduce">
<!--点击的时候执行showReduceTip方法-->
<svg class="specs_reduce_icon" v-if="foodNum" @click="showReduceTip">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#cart-minus" />
</svg>
</transition>
<transition name="fade">
<!--显示的数字,有点看不懂-->
<span class="cart_num" v-if="foodNum">{{foodNum}}</span>
</transition>
<!--点击的时候显示规则选择框-->
<span class="show_chooselist" @click="showChooseList(foods)">选规格</span>
</section>
</section>
<!--显示规格的情况 e-->
</section>
JS部分
export default{
data(){
return{
//控制下落的小圆点的显示和隐藏
showMoveDot:[]
}
},
mounted(){},
computed:{
//获取cartList,将this.cartList映射为this.$store.commit('add_cart')
...mapState(["cartList"]),
//监听cartList变化,更新当前商铺的购物车信息shopCart,同时返回一个新的对象
shopCart:function(){
return Object.assign({},this.cartList[this.shopId]);
},
//添加的食品的个数
foodNum:function(){
//类型id
let category_id = this.foods.category_id;
//项目id
let item_id = this.foods.item_id;
//这个地方判断的作用没搞懂,若它们都有值能代表什么呢
if(this.shopCart &&
this.shopCart[category_id] &&
this.shopCart[category_id][item_id]){
let num=0;
Object.values(this.shopCart[category_id][item_id]).forEach(
(item,index)=>{
num+item.num
}
);
return num
}
else{
return 0;
}
}
},
props: ["foods", "shopId"],
methods:{
...mapMutations(["ADD_CART", "REDUCE_CART"]),
//移出购物车
removeOutCart(category_id,
item_id,
food_id,
name,
price,
specs,
packing_fee,
sku_id,
stock){
if(this.foodNum>0){
//调用REDUCE_CART方法
this.REDUCE_CART({
shopid: this.shopId,
category_id,
item_id,
food_id,
name,
price,
specs,
packing_fee,
sku_id,
stock
});
}
},
//加入购物车
addToCart(
category_id,
item_id,
food_id,
name,
price,
specs,
packing_fee,
sku_id,
stock,
event){
this.ADD_CART({
shopid: this.shopId,
category_id,
item_id,
food_id,
name,
price,
specs,
packing_fee,
sku_id,
stock
});
let elLeft=event.target.getBoundingClientRect().left;
let elBottom=event.target.getBoundingClientRect().bottom;
this.showMoveDot.push(true);
this.$emit("showMoveDot",this.showMoveDot,elLeft,elBottom);
},
//显示规格列表
showChooseList(foodScroll){
this.$emit("showChooseList",foodScroll);
},
//点击多规格商品的减按钮,弹出提示
showReduceTip(){
this.$emit("showReduceTip");
}
}
}
shop.vue中引入buyCart.vue:
...
<section class="menu_right" ref="menuFoodList">
<ul>
<li v-for="(item,index) in menuList" :key="index">
...
<footer class="menu_detail_footer">
...
<!--showChooseList显示规格列表
showReduceTip:点击多规格商品的减按钮,弹出提示
showMoveDot:
传递过来的参数:this.$emit("showMoveDot",this.showMoveDot,elLeft,elBottom)
-->
<buy-cart :shopid="shopId" :foods="foods"
@moveInCart="listenInCart"
@showChooseList="showChooseList"
@showReduceTip="showReduceTip"
@showMoveDot="showMoveDotFun"
>
</buy-cart>
</footer>
</li>
</ul>
</section>
到此为止,规格和加号的布局已完整。功能实现收其他部分的影响,暂时不做说明。
参照项目地址:地址
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。