美文网首页
联动的demo

联动的demo

作者: LUOTAOLUOTAO | 来源:发表于2020-08-10 15:52 被阅读0次
    
    <template>
      <view class="class">
        <!-- 一级分类 -->
        <scroll-view :scroll-top="maxTitle.scrollTop" scroll-y scroll-with-animation class="class-left no-scrollbar">
          <view @click="maxTitleTab(item.id, index)" v-for="(item, index) in maxTitle.list" :key="index" :class="{ checked: maxTitle.current === index }" class="item">
            <view class="line-overflow">{{ item.name }}</view>
          </view>
        </scroll-view>
        <view class="class-right">
          <!-- 二级分类 -->
          <scroll-view :scroll-left="minTitle.scrollLeft" scroll-x scroll-with-animation style="width: 100%" class="no-scrollbar">
            <view class="class-nav">
              <view @click="minTitleTab(item.id, index)" v-for="(item, index) in minTitle.list" :key="index" :class="{ checked: minTitle.current === index }" class="min-title-item item">{{
                item.name
              }}</view>
            </view>
          </scroll-view>
          <scroll-view @scroll="onScroll" :scroll-top="listModule.scrollTop" scroll-with-animation scroll-y enable-back-to-top class="content-scroll">
            <block v-for="(item, index) in listModule.list" :key="index">
              <view v-if="item.products.length" class="list-modele class-model">
                <view class="class-name">{{ item.cname }}</view>
                <view class="class-content">
                  <view @click="toDetail(e.productId)" v-for="(e, i) in item.products" :key="i" class="product-item">
                    <view class="logo">
                      <image :src="e.littleImage" lazy-load mode="aspectFill" />
                    </view>
                    <view class="content">
                      <view class="product-name line-overflow">{{ e.onlyName }}</view>
                      <view class="tip-view">
                        <view v-for="(tag, a) in e.tags" :key="a" class="tip">{{ tag }}</view>
                      </view>
                      <view class="remain">销量 {{ e.saleAmount || 0 }} · 库存 {{ e.stockAmount || 0 }}</view>
                      <view class="sale-money">
                        <text class="money">{{ e.salePriceYuan || '0.00' }}</text>
                        <text class="old-money" style="font-size: 13px">¥{{ e.originPriceYuan || '0.00' }}</text>
                        <!-- <text v-if="e.agentMoney" class="return-money">{{ e.agentMoney }}</text> -->
                      </view>
                    </view>
                    <text v-if="e.isSoldOut === 1" class="status">已售罄</text>
                    <view v-else @click.stop="joinCart(`#join-cart${index}-${i}`, e, index, i)" class="join-cart" :id="`join-cart${index}-${i}`">
                      <image mode="widthFix" src="/static/images/home/join-cart.png"></image>
                      <view v-if="joinCartLoading[`${index}-${i}`]" @click.stop class="cart-loading"></view>
                    </view>
                  </view>
                </view>
              </view>
            </block>
          </scroll-view>
        </view>
    
        <!-- 购物车动画 -->
        <cart-animate ref="cartAnimate"></cart-animate>
      </view>
    </template>
    
    <script>
    import cartAnimate from '@/component/cartAnimate.vue'
    
    import { setCartNum, getProductCategories, getProductPageCategory, cartAdd } from '@/service/consumer'
    
    export default {
      name: 'class',
      components: {
        cartAnimate
      },
      data() {
        return {
          httpErr: false,
          maxTitle: {
            list: [],
            scrollTop: 0,
            current: 0
          }, // 一级分类
          minTitle: {
            list: [], // 数据
            current: 0, // 选中索引
            domtArr: [], // dom信息
            scrollLeft: 0
          }, // 二级标题
          listModule: {
            list: [],
            heightArr: [],
            scrollTop: 0
          }, // 分类数据
          iscartAnimate: true, // 允许加入购物车
          joinCartLoading: {} // 购物车加载
        }
      },
      onLoad() {
        this.getClassTitle()
      },
      onShow() {
        if (this.httpErr) this.getClassTitle() // onLoad 错误继续请求
        setCartNum() // 设置购物车数量
      },
      methods: {
        // 竖向滚动
        onScroll(e) {
          const scrollTop = e.detail.scrollTop
          let minTitleCurrent = 0
          this.listModule.heightArr.forEach((item, index) => {
            if (parseInt(scrollTop) >= parseInt(item)) minTitleCurrent = index
          })
          this.minTitle.current = minTitleCurrent
    
          this.minTitleScroll(minTitleCurrent) // 二级分类滚动
        },
        // 去详情
        toDetail(productId = '') {
          uni.navigateTo({
            url: `/pages/consumer/home/detail?productId=${productId}`
          })
        },
        // 二级菜单点击
        minTitleTab(id, index) {
          this.minTitle.current = index
          this.minTitleScroll(index) // 二级分类滚动
    
          const scrollTop = this.listModule.scrollTop
          let curScrollTop = this.listModule.heightArr[index]
          scrollTop === curScrollTop && curScrollTop++ // 解决小程序兼容性
          this.listModule.scrollTop = curScrollTop
        },
        // 一级菜单点击
        maxTitleTab(id, index) {
          this.minTitle.list = this.maxTitle.list[index].sons || []
          this.maxTitle.current = index
          this.getListModule(id)
          this.minTitle.current = 0 // 二级分类重置
          this.minTitle.scrollLeft = 0 // 二级分类滚动位置重置
          this.listModule.scrollTop = 0 // 竖向滚动重置
          this.getMinTitleItemDom() //  获取二级标题dom信息集合
    
          if (index >= 2) {
            this.maxTitle.scrollTop = (index - 1) * uni.upx2px(126)
          } else {
            this.maxTitle.scrollTop = 0
          }
        },
        // 加入购物车
        joinCart(joinCartDom, row = {}, index = 0, i = 0) {
          if (!this.iscartAnimate)
            return uni.showToast({
              title: '别着急,慢慢来',
              duration: 1000,
              icon: 'none'
            })
          this.$set(this.joinCartLoading, `${index}-${i}`, true) // 当前加入loading显示
          this.iscartAnimate = false
          const { agentId, productId, productItemId } = row
          const body = {
            agentId,
            productId,
            productItemId,
            quantity: 1
          }
          cartAdd(body)
            .then(() => {
              this.$refs.cartAnimate.joinCart(joinCartDom) // 执行购物车动画
            })
            .finally(() => {
              this.iscartAnimate = true
              this.$set(this.joinCartLoading, `${index}-${i}`, false) // 当前加入loading关闭
              setCartNum() // 设置购物车数量
            })
        },
        // 获取分类列表
        getListModule(id = '') {
          this.listModule.list = []
          getProductPageCategory({ concurrentId: id }).then((res = {}) => {
            const { records = [] } = res
            this.listModule.list = records
    
            // 计算高度
            this.$nextTick(() => {
              uni
                .createSelectorQuery()
                .selectAll('.list-modele')
                .boundingClientRect((rect) => {
                  let height = 0
                  const domArr = []
                  rect.forEach((item) => {
                    domArr.push(height)
                    height += item.height
                  })
                  this.listModule.heightArr = domArr
                  console.log(this.listModule.heightArr, 'listModule')
                })
                .exec()
            })
          })
        },
        // 获取分类
        getClassTitle() {
          getProductCategories()
            .then((res = {}) => {
              const { records = [] } = res
              this.maxTitle.list = records
              console.log(this.maxTitle)
              // 默认列表渲染
              if (records.length) {
                this.minTitle.list = records[0].sons || []
                this.getListModule(records[0].id)
              }
            })
            .catch(() => {
              this.httpErr = true
            })
        },
        // 二级标题滚动
        minTitleScroll(index = 0) {
          if (index >= 2) {
            const domtArr = this.minTitle.domtArr
            const uselessLeft = domtArr[0].left // 无用的left
            this.minTitle.scrollLeft = domtArr[index].left - uselessLeft - (domtArr[index - 1].width + uni.upx2px(20))
            console.log('scrollLeft', this.minTitle.scrollLeft)
          } else {
            this.minTitle.scrollLeft = 0
          }
        },
        // 获取二级标题dom信息集合
        getMinTitleItemDom() {
          this.$nextTick(() => {
            uni
              .createSelectorQuery()
              .selectAll('.min-title-item')
              .boundingClientRect((rect) => {
                console.log('获取二级标题dom信息集合', rect)
                this.minTitle.domtArr = rect
              })
              .exec()
          })
        }
      }
    }
    </script>
    
    <style lang="scss" scoped>
    ::-webkit-scrollbar {
      width: 0;
      height: 0;
      color: transparent;
    }
    .class {
      height: 100%;
      display: flex;
      &-left {
        margin-top: 20rpx;
        width: 170rpx;
        height: calc(100% - 20rpx);
        background: hsl(0, 0%, 96%);
        .item {
          position: relative;
          width: 100%;
          height: 126rpx;
          padding: 0 15rpx;
          box-sizing: border-box;
          display: flex;
          align-items: center;
          justify-content: center;
          font-weight: bold;
          font-size: 26rpx;
          transition: background-color 0.3s;
          &::after {
            content: '';
            position: absolute;
            top: 50%;
            left: 0;
            width: 4rpx;
            height: 0;
            background: $main-color;
            transform: translateY(-50%);
            transition: height 0.3s;
          }
        }
        .checked {
          background: #fff;
          &::after {
            // content: '';
            height: 50rpx;
          }
        }
      }
      &-right {
        flex: 1;
        height: 100%;
        padding-top: 20rpx;
        padding-left: 30rpx;
        box-sizing: border-box;
        overflow: hidden;
        .class-nav {
          padding-bottom: 30rpx;
          display: inline-block;
          white-space: nowrap;
          .item {
            margin-right: 20rpx;
            min-width: 80rpx;
            height: 50rpx;
            border-radius: 5rpx;
            background: #f4f4f4;
            padding: 0 20rpx;
            display: inline-block;
            text-align: center;
            line-height: 50rpx;
            font-weight: 500;
            font-size: 26rpx;
            // transition: background-color 0.2s;
          }
          .checked {
            background: $main-color;
            color: #fff;
          }
        }
        .content-scroll {
          height: calc(100% - 80rpx);
        }
        .class-model {
          padding-bottom: 30rpx;
          .class-name {
            padding: 20rpx 0 0 0;
            font-weight: bold;
            font-size: 24rpx;
          }
          .class-content {
            padding-right: 30rpx;
            .product-item {
              position: relative;
              border-bottom: solid 1rpx #f2f2f2;
              padding: 50rpx 0;
              display: flex;
              .logo {
                margin-right: 30rpx;
                flex-shrink: 0;
                width: 150rpx;
                height: 150rpx;
                background: #f4f4f4;
                image {
                  width: 100%;
                  height: 100%;
                }
              }
              .content {
                width: calc(100% - 180rpx);
                .product-name {
                  height: 46rpx;
                  box-sizing: border-box;
                  width: 100%;
                  line-height: 1.5;
                  font-weight: 500;
                  font-size: 26rpx;
                }
                .tip-view {
                  height: 30rpx;
                  display: flex;
                  align-items: center;
                  .tip {
                    margin-right: 10rpx;
                    border-radius: 2rpx;
                    background: rgba(242, 109, 48, 0.1);
                    padding: 0 15rpx;
                    line-height: 30rpx;
                    font-weight: bold;
                    font-size: 23rpx;
                    color: #f26d30;
                  }
                }
                .remain {
                  flex-shrink: 0;
                  height: 32rpx;
                  padding-top: 15rpx;
                  box-sizing: border-box;
                  line-height: 1;
                  font-size: 23rpx;
                  color: #ccc;
                }
                .sale-money {
                  padding-top: 6rpx;
                  .money {
                    position: relative;
                    top: 2px;
                  }
                  .old-money {
                    position: relative;
                    top: 1px;
                    padding-left: 15rpx;
                    display: inline-block;
                    font-size: 12px;
                    color: #ccc;
                    text-decoration: line-through;
                  }
                  .return-money {
                    position: relative;
                    top: -6rpx;
                    margin-left: 15rpx;
                    background: #f3b446;
                    padding: 0 15rpx;
                    display: inline-block;
                    line-height: 30rpx;
                    font-weight: bold;
                    font-size: 12px;
                    color: #fff;
                  }
                }
              }
              .status {
                position: absolute;
                right: 0;
                bottom: 50rpx;
                font-size: 13px;
                color: #ccc;
              }
              .join-cart {
                position: absolute;
                right: -20rpx;
                bottom: 26rpx;
                padding: 27rpx 28rpx;
                image {
                  width: 45rpx;
                  height: 45rpx;
                }
                .cart-loading {
                  position: absolute;
                  top: 0;
                  left: 0;
                  width: 100%;
                  height: 100%;
                  background: rgba(255, 255, 255, 0.8);
                  &::after {
                    content: '';
                    position: absolute;
                    top: 0;
                    right: 0;
                    bottom: 0;
                    left: 0;
                    margin: auto;
                    width: 36rpx;
                    height: 36rpx;
                    border-radius: 50%;
                    border-bottom: solid 2rpx $main-color;
                    box-sizing: border-box;
                    animation: turn 1s linear infinite;
                  }
                }
              }
            }
          }
        }
      }
    }
    </style>
    

    相关文章

      网友评论

          本文标题:联动的demo

          本文链接:https://www.haomeiwen.com/subject/kdejdktx.html