food-detail-list的内容:vue
... <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的数据结构以下:git
... <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部分:github
export default{ data(){ return{ ... //点击展现列表头部详情 TitleDetailIndex:null } }, //说明信息的隐藏和显示 showTitleDetail(index){ if(this.TitleDetailIndex==index){ this.TitleDetailIndex=null; } else{ this.TitleDetailIndex=index; } } }
若标识规格的参数specifications没有长度的时候显示+号,若标识规格的参数specifications有长度时显示"规格"。
须要用到Vuex。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是相似的,不一样之处在于:数据结构
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中定义方法:svg
//引入常量 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>
到此为止,规格和加号的布局已完整。功能实现收其余部分的影响,暂时不作说明。
参照项目地址:地址