外卖webAPP(四)
一,评价shopRtings页面搭建
在vuex中获取数据,商家评价列表数据
mounted(){ this.$store.dispatch('getShopRatings') }, computed:{ ...mapState(['info', 'ratings']) }
填充数据, 根据rateType的标识来来判断是向上的拇指还是向下的拇指
<div class="recommend"> <span class="iconfont" :class="rating.rateType===0 ? 'icon-thumb_up' : 'icon-thumb_down'"></span> <span class="item" v-for="(item, index) in rating.recommend" :key="index">{{item}}</span> </div>
评论信息可以点击滚动,需要在获取数据后(vuex中通知评价组件),创建better-scroll实例
mounted () { this.$store.dispatch('getShopRatings', () => { this.$nextTick(() => { new BScroll(this.$refs.ratings, { click: true }) }) }) },
// 异步获取商家评价列表 async getShopRatings({commit}, callback) { const result = await reqShopRatings() if (result.code === 0) { const ratings = result.data commit(RECEIVE_RATINGS, {ratings}) // 数据更新了, 通知一下组件 callback && callback() } },
关于内容的过滤,全部,满意,不满意,或者是否有内容,这两种条件来过滤评论内容
全部的数量,满意的数量,不满意的数量
在ratings数组中每个对象都有个rateType属性,0为满意,1为不满意,在vuex中的getters中计算满意的个数
positiveSize (state) { return state.ratings.reduce((preTotal, rating) => preTotal + (rating.rateType===0?1:0) , 0) }
填充模板
...mapGetters(['positiveSize']),
<div class="ratingselect"> <div class="rating-type border-1px"> <span class="block positive" :class="{active: selectType===2}" @click="setSelectType(2)"> 全部<span class="count">{{ratings.length}}</span> </span> <span class="block positive" :class="{active: selectType===0}" @click="setSelectType(0)"> 满意<span class="count">{{positiveSize}}</span> </span> <span class="block negative" :class="{active: selectType===1}" @click="setSelectType(1)"> 不满意<span class="count">{{ratings.length-positiveSize}}</span> </span> </div> <div class="switch" :class="{on: onlyShowText}" @click="toggleOnlyShowText"> <span class="iconfont icon-check_circle"></span> <span class="text">只看有内容的评价</span> </div> </div>
类名on,代表只有内容的评价是否勾选,定义一个变量onlyShowText默然勾选,点击勾选后,标识状态取反
data () { return { onlyShowText: true, // 是否只显示有文本的 selectType: 2 , // 选择的评价类型: 0满意, 1不满意, 2全部 } },
toggleOnlyShowText () { this.onlyShowText = !this.onlyShowText }
点击满意,不满意,全部按钮,定义一个变量selectType来标识他们的意思, 选择的评价类型: 0满意, 1不满意, 2全部, 默认全部
setSelectType (selectType) { this.selectType = selectType },
过滤数组,通过两个条件,来过滤筛选之后的数组
filterRatings () { // 得到相关的数据 const {ratings, onlyShowText, selectType} = this // 产生一个过滤新数组 return ratings.filter(rating => { const {rateType, text} = rating /* 条件1: selectType: 0/1/2 rateType: 0/1 selectType===2 || selectType===rateType 条件2 onlyShowText: true/false text: 有值/没值 !onlyShowText || text.length>0 */ return (selectType===2 || selectType===rateType) && (!onlyShowText || text.length>0) }) }
填充模板
<div class="rating-wrapper"> <ul> <li class="rating-item" v-for="(rating, index) in filterRatings" :key="index"> <div class="avatar"> <img width="28" height="28" :src="rating.avatar"> </div> <div class="content"> <h1 class="name">{{rating.username}}</h1>
二,商家shopInfo组件页面搭建
2.1.,从vuex中获取数据info
mounted(){ this.$store.dispatch('getShopInfo') }, computed:{ ...mapState(['info']) }
填充数据
获取服务的标签按钮有三个不同颜色,而获取的数据info.supports数组中,遍历的对象都有type属性,0代表绿色, 1红色,2黄色
我们需要将标签颜色动态渲染,将三个颜色类名定义一个数组,利用type属性来一一对应
data () { return { supportClasses: ['activity-green', 'activity-red', 'activity-orange'] } },
<section class="section"> <h3 class="section-title">活动与服务</h3> <div class="activity"> <div class="activity-item" v-for="(support, index) in info.supports" :key="index" :class="supportClasses[support.type]"> <span class="content-tag"> <span class="mini-tag">{{support.name}}</span> </span> <span class="activity-content">{{support.content}}</span> </div> </div> </section>
方法有两种
第一种,在mouted阶段,dispatch获取到info数据,传一个回调函数,然后vuex中actions发送请求,获取到info数据,在调用这个回调,通知商家页面去做创建scroll的逻辑
mounted(){ // 数据回来了,创建bette-scroll this.$store.dispatch('getShopInfo',()=>{ this.$nextTick(()=>{ // info页面滚动 new BScroll('.shop-info',{ click : true }) // 获取uldom对象 const ulNode = this.$refs.picsUl const liWidth = 120 const space = 6 // 图片数量 const counts = this.info.pics.length // 整个图片盒子长度 ulNode.style.width = (liWidth + space) *counts -space +'px' console.log(ulNode.style.width) // 商家实景图片滚动,水平滚动 new BScroll('.pic-wrapper',{ // click: true, scrollX :true }) })
<section class="section"> <h3 class="section-title">商家实景</h3> <div class="pic-wrapper"> <ul class="pic-list" ref="picsUl"> <li class="pic-item" v-for="(pic, index) in info.pics" :key="index"> <img width="120" height="90" :src="pic"/> </li> </ul> </div> </section>
// 异步获取商家信息 async getShopInfo({ commit },callback) { const result = await reqShopInfo() if (result.code === 0) { const info = result.data commit('RECEIVE_INFO', info) callback && callback() } },
此时实景图片不会滚动,原因是ul父元素的宽度347, 而而li中的图片宽度120,右边距6, 父元素ul不是自适应li的宽度,
解决,我们需要根据li计算ul的宽度, ulNode.style.width = (liWidth + space) *counts -space +'px'
注:1.切换回路由组件,组件生命周期消失了又创建了,会重新执行mounted钩子函数(数据和页面已经都回来了),不会执行watch
2.如果页面刷新了,不会重新执行mounted,需要监听数据变化,执行wacth监听,此时还要先执行this.nextTick()函数,
mounted () {
// 如果数据还没有, 直接结束 if(!this.info.pics) { return } // 数据有了, 可以创建BScroll对象形成滑动 this._initScroll() }, methods: { _initScroll () { new BScroll('.shop-info') // 动态计算ul的宽度 const ul = this.$refs.picsUl const liWidth = 120 const space = 6 const count = this.info.pics.length ul.style.width = (liWidth + space) * count -space + 'px' new BScroll('.pic-wrapper', { scrollX: true // 水平滑动 }) } }, watch: { info () {// 刷新流程--> 更新数据 this.$nextTick(() => { this._initScroll() }) } }
三,搜索search页面搭建
从vuex中获取搜索商家列表数据,需要传递搜索关键字
当点击提交按钮(阻止form表单自己提交prevent),dispatch到vuex,获取数据
<form class="search_form" @submit.prevent="search"> <input type="search" placeholder="请输入商家名称" class="search_input" v-model="keyword"> <input type="submit" class="search_submit"> </form>
methods:{ search(){ if(this.keyword.trim()){ this.$store.dispatch('searchShops',this.keyword ) } } }, computed:{ ...mapState(['searchShops']) }
填充数据
注,点击提交按钮时,当有数据的时候,显示数据,没有数据时,显示很抱歉!无搜索结果
此时,出现一个小bug,当第一次search页面渲染时,没有点击提交按钮,还是显示很抱歉,无搜索结果,需要判断
定义一个标识变量noSearchShops默认false,不显示数据。之后点击提交按钮,需要监视searchShops数据,有新数据,noSearchShops为false, 没有数据为
data() { return { keyword:'', imgBaseUrl: 'http://cangdu.org:8001/img/', noSearchShops:false } },
watch: { // 监视searchShop数据 searchShops (value) { if(!value.length) { // 没有数据 this.noSearchShops = true } else {// 有数据 this.noSearchShops = false } } },
如果router-link被浏览器渲染时a标签,但是如果把它改成li标签,加一个tag属性
<section class="list" v-if="!noSearchShops"> <ul class="list_container"> <!--:to="'/shop?id='+item.id"--> <router-link :to="{path:'/shop', query:{id:item.id}}" tag="li" v-for="item in searchShops" :key="item.id" class="list_li"> <section class="item_left"> <img :src="imgBaseUrl + item.image_path" class="restaurant_img" style="width:50px"> </section>