商品列表布局

左侧宽度固定,右边自适应
flex: 0 0 80px
第一个是等分,第二个是缩放,第三个是占位空间

<div class="goods">
  <div class="menu-wrapper"></div>
  <div class="foods-wrapper"></div>
</div>
--------------------css------------------------
.goods
  display: flex
  position: absolute
  top: 174px
  bottom: 46px
  width: 100%
  .menu-wrapper
    flex: 0 0 80px
    width: 80px
    background: #ccc
   .foods-wrapper
      flex: 1
--------------------js获取数据------------------
const ERR_OK = 0
export default {
  props: {
    seller: {
      type: Object
    }
  },
  data() {
    return {
      goods: []
    }
  },
  created: {
    this.$http.get('/api/goods').then((res) => {
      res = res.body
      if(res.errno === ERR_OK){
        this.goods = res.data
    }
  })
  }
}

左侧布局

可能有多行垂直居中,适合用display:table

<ul>
<li class="menu-item" v-for="item in goods" >
  <span class="text border-1px">
    <span v-show="item.type>0" class="icon" :class="classMap[item.type]"></span>
    {{ item. name}}
  </span>
</li>
</ul>
-------------js----------------------------------------------
export default {
  created:() {
    this.classMap = ['decrease','discount','special','invoice','guarantee'];
  }
}
-------------css----------------------------------------------
bg-image($url)
    background-image: url($url + "@2x.png")
    @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3)
        background-image: url($url + "@3x.png")
            .menu-item
                display: table
                width: 56px
                height: 54px
                padding: 0 12px
                line-height: 14px
                .icon
                    display: inline-block
                    vertical-align: top
                    width: 12px
                    height: 12px
                    margin-right: 2px
                    background-size: 12px 12px
                    background-repeat: no-repeat
                    &.decrease
                        bg-image('decrease_3')
                    &.discount
                        bg-image('discount_3')
                    &.guarantee
                        bg-image('guarantee_3')
                    &.invoice
                        bg-image('invoice_3')
                    &.special
                        bg-image('special_3')
                .text
                    display: table-cell
                    width: 56px
                    vertical-align: middle
                    border-1px(rgba(7,17,27,0.2)) 
                    font-size: 12px

右侧布局

       <div class="foods-wrapper">
           <ul>
               <li v-for="item in goods" class="food-list">
                   <h1 class="title">{{ item.name }}</h1>
                   <ul>
                       <li v-for="food in item.foods" class="food-item">
                           <div class="icon">
                               <img :src="food.icon">
                           </div>
                           <div class="content">
                               <h2 class="name">{{ food.name}}</h2>
                               <p class="desc">{{food.description}}</p>
                               <div class="extra">
                                   <span>月售{{ food.sellCount }}份</span>
                                   <span>好评率{{ food.rating }}%</span>
                               </div>
                               <div class="price">
                                   <span>¥{{ food.price }}</span>
                                   <span v-show="food.oldPrice">¥{{ food.oldPrice }}</span>
                               </div>
                           </div>
                       </li>
                   </ul>
               </li>
           </ul>
       </div>
--------------------------css-------------------------------

   .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
               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
                       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)

怎么把1像素的边去掉?
在mixin.styl中写:

border-none()
    &:after
        display: none

better-scroll应用

  • 安装
    cnpm install better-scroll --save
  • 使用
    import BScroll from 'better-scroll'
    vue取dom
    <div class="menu-wrapper" ref="menuWrapper"></div>
      created() {
            this.classMap = ['decrease','discount','special','invoice','guarantee'];
            this.$http.get('/api/goods').then((res) => {
                res = res.body
                if(res.errno === ERR_OK){
                    this.goods = res.data
                    this.$nextTick(() => {
//this.$nextTick() => 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
//数据发生变化后,不能直接更新在dom上,需要在回调函数中刷新DOM,即异步加载DOM
                        this._initScroll()
                    })
                    
                }
            })
        },
        methods: {
            _initScroll() {
                //this.$refs:取得DOM对象
                this.menuScroll = new BScroll(this.$refs.menuWrapper, {})
                this.foodsScroll = new BScroll(this.$refs.foodsWrapper, {})

            }
        }

如果报错
Error in nextTick: "TypeError: Cannot read property 'children' of undefined"
那么就是因为
<div class="menu-wrapper" ref="menuWrapper"></div>
menuWrapper要用驼峰写,不要用-

实现左右联动

右边纵坐标落到哪个 区间,计算每个区间的高度
然后返回右边的索引 与 左边的索引相等时,给左侧Li高亮

在`$nextTick`拿到数据,dom更新后
data() {
  return {
    listHeight: [],
    scrollY: 0
 }
},
computed: {
  currentIndex() {
    for(let i = 0; i < listHeight.length; i++){
      let height1 = listHeight[i];
      let height2 = listHeight[i + 1]
      if(!height2 || this.scrollY >= height1 && this.scrollY < height2){
        return i
      }
     }
    return 0
}
},
created: {
this.$nextTick(function(){
  this._initScroll();
  this._calculateHeight();
})
},
methods: {
  _initScroll() {
     this.menuScroll = new BScroll(this.$refs.menuWrapper, {
      click: true //取消默认阻止事件    
     });
     this.foodsScroll = new BScroll(this.$refs.foodsWrapper, {
      click: true,
      probeType: 3 //监听事件的触发时间,1为即时触发,3为延迟触发
    });
     this.foodsScroll.on('scroll', () => {
        this.scrollY = Math.abs(Math.round(pos.y))
     })
},
  _calculateHeight(){
    var foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook')
    let height = 0
    for(var i = 0; i < foodList.length; i++){
      let item = foodList[i]//获取每个li的高度
      height += item.clientHeight;
      this.listHeight.push(height);
}
}
}

把右边当前的索引关联到左侧导航的索引
<li v-for="(item,index) in goods" class="menu-item" :class="{'current':currentIndex === index}"></li>
  • 点击左侧菜单 实现右侧联动
index:当前索引值
$event://当pc浏览器会触发两次,解决方法传入event
<li v-for="(item,index) in goods" class="menu-item" :class="{'current':currentIndex === index}" @click="selectMenu(index,$event)"></li>

methods: {
  selectMenu(index,event) {
    if(!event._constructed){//阻止非vue事件
      return
    }
    var foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook');
    let el = foodList[index]//获取右侧当前位置
    this.foodsScroll.scrollToElement(el, 300)//右侧内容指向右侧当前位置
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 常见的页面布局 作为一个像我这样的切图仔前端而言,拿到设计图的第一步就是要分析设计图大致地划分区域,然后选择一种最...
    自度君阅读 1,203评论 0 1
  • 现在,对于微信公众号,大家都不陌生。谁微信没关注几个自己感兴趣的公众号呢。随着微信公众号的普及,不再是13,14年...
    8976d908eb13阅读 5,744评论 0 1
  • 由于小时候我家住在一条大马路旁边,又靠近一个集市,我每天早上都会看到从灰蒙蒙的晨雾中从四面八方涌来的人群,人们提着...
    南城半世阅读 781评论 3 7
  • 初萌的日光 自微翘起的檐角 缓缓淌下 秋风的落叶 泥静的小路 带走了依依不舍的嬉戏 母亲远处呼喊的声音 院里说故事...
    小刘少爷阅读 203评论 0 4