一、前言
二、主要内容
1、实现效果(其实可以直接在父组件中操作子组件的显示隐藏,但是这里通过在子组件定义自己的显示隐藏效果,让父组件调用,训练一下这种方式)
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZuYjMwgzM5EzMy0CNxIDNyYjMxIzMxUDM5EDMy0iMzADM3MTMvwVNwkTMwIzLcJzMwAzNzEzLcd2bsJ2Lc12bj5ycn9Gbi52YugTMwIzZtl2Lc9CX6MHc0RHaiojIsJye.gif)
2、分析:
(1)点击父组件的某一个li项,跳出这个商品详情(子组件项)
(2)子组件中还是需要接收到父组件中的food,但是这个food不像上一篇那样是固定的,所以这里的这个food是根据我们点击的不同的项,传进去的
(3)为了实现上一步分析:我们需要在data中定义一个对象,点击的时候,将当前对象的food传进去,然后在传给子组件,这样就能实现动态传递food了
3、具体实现
(1)父组件中先引入并且使用子组件
(2)在父组件的每一项上添加一个点击事件,并且将当前项的food通过定义的这个函数传给子组件
<!--点击的时候将当前的food传进去-->
<li class="food-item bottom-border-1px" v-for="(food, index) in good.foods" :key="index" @click="showFood(food)">
//省略
</div>
</div>
</li>
(3)metods:中定义这个方法接收并传过去
<template>
<Food :food="food" ref="food"></Food>
</template>
<script>
data(){
return{
scrollY:0,
tops:[], //存放每一个类的初始位置
food:{}
}
}
components:{
Food
}
// 显示点击的food
showFood (food) {
// 设置food
this.food = food
}
</script>
(4)在子组件中要定义该组件的显示隐藏方法,先用一个标识来标记
export default {
props: {
food: Object
},
data () {
return {
isShow: false
}
},
methods: {
toggleShow () {
this.isShow = !this.isShow
}
},
components: {
CartControl
}
}
(5)父组件中用this.$refs.refname.method()来得到子组件的方法,并且执行
// 显示点击的food
showFood (food) {
// 设置food
this.food = food
// 显示food组件 (在父组件中调用子组件对象的方法)
this.$refs.food.toggleShow()
}
4、完整代码:
父组件:
<template>
<div>
<div class="goods">
<div class="menu-wrapper" >
<ul>
<!--current-->
<li class="menu-item " v-for="(good,index) in goods" :key="index" :class="{current:index===currentIndex}" @click="clickMenuItem(index)">
<span class="text bottom-border-1px">
<img class="icon" :src="good.icon" v-if="good.icon" >
{{good.name}}
</span>
</li>
</ul>
</div>
<div class="foods-wrapper">
<ul ref="foodsUl">
<li class="food-list-hook" v-for="(good, index) in goods" :key="index" >
<h1 class="title">{{good.name}}</h1>
<ul>
<li class="food-item bottom-border-1px" v-for="(food, index) in good.foods" :key="index" @click="showFood(food)">
<div class="icon">
<img width="57" height="57" :src="food.icon">
</div>
<div class="content">
<h2 class="name">{{food.name}}</h2>
<p class="desc">{{food.description}}</p>
<div class="extra">
<span class="count">月售{{food.sellCount}}份</span>
<span>好评率{{food.rating}}%</span>
</div>
<div class="price">
<span class="now">¥{{food.price}}</span>
<span class="old" v-if="food.oldPrice">¥{{food.oldPrice}}</span>
</div>
<div class="cartcontrol-wrapper">
<CartControl :food='food'></CartControl>
</div>
</div>
</li>
</ul>
</li>
</ul>
</div>
<Food :food="food" ref="food"></Food>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
import BScroll from 'better-scroll'
import CartControl from '../../../components/CartControl/CartControl'
import Food from '../../../components/Food/Food'
export default {
data(){
return{
scrollY:0,
tops:[], //存放每一个类的初始位置
food:{}
}
},
components:{
CartControl,
Food
},
//这里的数据是异步显示的,所以我们要等数据异步请求之后再创建这个滑动列表
mounted(){
//异步请求可以传过去两个参数,
this.$store.dispatch('getShopGoods',()=>{
//数据请求完之后再执行这里了
//初始化滚动
this.$nextTick(()=>{
//初始化,并且实时获取滚动坐标
this._initScrollY()
//初始化右边的数组
this._initTops();
})
})
},
methods:{
//初始化BScroll
_initScrollY(){
new BScroll('.menu-wrapper',{
click:true
})
//创建右边的
this.foodswrapper = new BScroll('.foods-wrapper',{
click:true,
probeType:3
})
//给右侧绑定的BScroll绑定监听事件,但是你会发现并没有调用
this.foodswrapper.on('scroll',({x,y})=>{
console.log(x,y)//默认没有分发滚动事件
this.scrollY=Math.abs(y);
})
//获取停下来的位置
//给右侧绑定的BScroll绑定监听事件,但是你会发现并没有调用
this.foodswrapper.on('scrollEnd',({x,y})=>{
//console.log(x,y)//默认没有分发滚动事件
this.scrollY=Math.abs(y);
})
}
//初始化数组,获取到每个li 的坐标
,_initTops(){
var tops=[] //定义一个空数组
let top=0;
tops[0]=0 //第一个li的坐标为0
var lis = this.$refs.foodsUl.children; //获取到了每个li
Array.prototype.slice.call(lis).forEach((li,index)=>{
top = top + li.clientHeight//当前的位置,等于上一个的位置,加上这一个的高度
tops.push(top)
})
this.tops=tops
console.log(tops)
},
//将当前的index传进来
clickMenuItem(index){
//先得到目标位置scrollY
const top = this.tops[index];
// 立即更新scrollY,更新当前分类,点击的分类项成为当前
this.scrollY=top
//平滑滚动右侧列表
this.foodswrapper.scrollTo(0, -top, 3);
},
// 显示点击的food
showFood (food) {
// 设置food
this.food = food
// 显示food组件 (在父组件中调用子组件对象的方法)
this.$refs.food.toggleShow()
}
},
computed:{
...mapState(['goods']),
currentIndex(){
return this.tops.findIndex((top,index)=>{
return this.scrollY>=top && this.scrollY<this.tops[index+1]
})
}
}
}
</script>
<style stylus" rel="stylesheet/stylus">
@import "../../../common/stylus/mixins.styl"
.goods
display: flex
position: absolute
top: 195px
bottom: 46px
width: 100%
background: #fff;
overflow: hidden
.menu-wrapper
flex: 0 0 80px
width: 80px
background: #f3f5f7
.menu-item
display: table
height: 54px
width: 56px
padding: 0 12px
line-height: 14px
&.current
position: relative
z-index: 10
margin-top: -1px
background: #fff
color: $green
font-weight: 700
.text
border-none()
.icon
display: inline-block
vertical-align: top
width: 12px
height: 12px
margin-right: 2px
background-size: 12px 12px
background-repeat: no-repeat
.text
display: table-cell
width: 56px
vertical-align: middle
bottom-border-1px(rgba(7, 17, 27, 0.1))
font-size: 12px
.foods-wrapper
flex: 1
.title
padding-left: 14px
height: 26px
line-height: 26px
border-left: 2px solid #d9dde1
font-size: 12px
color: rgb(147, 153, 159)
background: #f3f5f7
.food-item
display: flex
margin: 18px
padding-bottom: 18px
bottom-border-1px(rgba(7, 17, 27, 0.1))
&:last-child
border-none()
margin-bottom: 0
.icon
flex: 0 0 57px
margin-right: 10px
.content
flex: 1
.name
margin: 2px 0 8px 0
height: 14px
line-height: 14px
font-size: 14px
color: rgb(7, 17, 27)
.desc, .extra
line-height: 10px
font-size: 10px
color: rgb(147, 153, 159)
.desc
line-height: 12px
margin-bottom: 8px
.extra
.count
margin-right: 12px
.price
font-weight: 700
line-height: 24px
.now
margin-right: 8px
font-size: 14px
color: rgb(240, 20, 20)
.old
text-decoration: line-through
font-size: 10px
color: rgb(147, 153, 159)
.cartcontrol-wrapper
position: absolute
right: 0
bottom: 12px
</style>
父组件.vue
子组件:
<template>
<div class="food" v-if="isShow">
<div class="food-content">
<div class="image-header">
<img :src="food.image">
<p class="foodpanel-desc">{{food.info}}</p>
<div class="back" @click="toggleShow">
<i class="iconfont icon-arrow_left"></i>
</div>
</div>
<div class="content">
<h1 class="title">{{food.name}}</h1>
<div class="detail">
<span class="sell-count">月售{{food.sellCount}}份</span>
<span class="rating">好评率{{food.rating}}%</span>
</div>
<div class="price">
<span class="now">¥{{food.price}}</span>
<span class="old" v-show="food.oldPrice">¥{{food.oldPrice}}</span>
</div>
<div class="cartcontrol-wrapper">
<CartControl :food="food"/>
</div>
</div>
</div>
<div class="food-cover" @click="toggleShow"></div>
</div>
</template>
<script>
import CartControl from '../CartControl/CartControl.vue'
export default {
props: {
food: Object
},
data () {
return {
isShow: false
}
},
methods: {
toggleShow () {
this.isShow = !this.isShow
}
},
components: {
CartControl
}
}
</script>
<style stylus" rel="stylesheet/stylus" scoped>
@import "../../common/stylus/mixins.styl"
.food
position: fixed
left: 0
top: 0
bottom: 48px
z-index: 101
width: 100%
&.fade-enter-active, &.fade-leave-active
transition opacity .5s
&.fade-enter, &.fade-leave-to
opacity 0
.food-content
position absolute
left 50%
top 50%
transform translate(-50%, -50%)
width 80%
height 65%
z-index 66
background #fff
border-radius 5px
.image-header
position: relative
width: 100%
height: 0
padding-top: 100%
img
position: absolute
top: 0
left: 0
width: 100%
height: 100%
.foodpanel-desc
font-size 10px
color #ddd
letter-spacing 0
position absolute
bottom 0
left 0
right 0
padding 0 10px 10px
.back
position: absolute
top: 10px
left: 0
.icon-arrow_left
display: block
padding: 10px
font-size: 20px
color: #fff
.content
position: relative
padding: 18px
.title
line-height: 14px
margin-bottom: 8px
font-size: 14px
font-weight: 700
color: rgb(7, 17, 27)
.detail
margin-bottom: 18px
line-height: 10px
height: 10px
font-size: 0
.sell-count, .rating
font-size: 10px
color: rgb(147, 153, 159)
.sell-count
margin-right: 12px
.price
font-weight: 700
line-height: 24px
.now
margin-right: 8px
font-size: 14px
color: rgb(240, 20, 20)
.old
text-decoration: line-through
font-size: 10px
color: rgb(147, 153, 159)
.cartcontrol-wrapper
position: absolute
right: 12px
bottom: 12px
.buy
position: absolute
right: 18px
bottom: 18px
z-index: 10
height: 24px
line-height: 24px
padding: 0 12px
box-sizing: border-box
border-radius: 12px
font-size: 10px
color: #fff
background: rgb(0, 160, 220)
&.fade-transition
transition: all 0.2s
opacity: 1
&.fade-enter, &.fade-leave
opacity: 0
.food-cover
position absolute
top 0
right 0
bottom -48px
left 0
z-index 55
background-color rgba(0, 0, 0, 0.5)
</style>
子组件.vue
三、总结
转载于:https://www.cnblogs.com/xxm980617/p/10859233.html