三级联动开发(非城市,类似美团)

三级联动开发(非城市,类似美团)
工作要求,需要写一个三级联动,最终写的代码,效果如下
1.默认情况展示附近(无子列表),全部分类(有子列表),离我最近(无子列表),筛选(无子列表),
在这里插入图片描述
2.点击每个tabItem展示下拉框,如下图
在这里插入图片描述
3.点击元素反显在tab上,如果没有子列表弹窗收回
在这里插入图片描述
如果有子列表,点击每一项出现它对应的子列表
在这里插入图片描述
基本的情况就是这样,下面上代码,这个tabbar是根据mandmoblie的下拉组件改造的(dropMenu)

数据结构
defaultList: [
        {
          type: '附近',
          firstList: [{ title: '附近',level: '1', }, { title: '500m',level: '1', }, { title: '1000m',level: '1', }, { title: '3000m',level: '1', }, { title: '5000m',level: '1', }],
        },
        {
          type: '全部分类',
          firstList: [
            {
              title: '旅游出差',
              level: '1',
              childList: [
                {
                  title: '旅游',
                  level: '2',
                  childList: [
                    {
                      title: '去哪',
                      level: '3',
                    },
                    {
                      title: '飞猪',
                      level: '3',
                    },
                  ],
                },
                {
                  title: '出差',
                  level: '2',
                  childList: [
                    {
                      title: '飞机',
                      level: '3',
                    },
                    {
                      title: '火箭',
                      level: '3',
                    },
                  ],
                },
              ],
            },
            {
              title: '百货零售',
              level: '1',
              childList: [
                {
                  title: '家装建材',
                  level: '2',
                  childList: [
                    {
                      title: '家装',
                      level: '3',
                    },
                    {
                      title: '建材',
                      level: '3',
                    },
                  ],
                },
                {
                  title: '食品鲜花',
                  level: '2',
                  childList: [
                    {
                      title: '食品',
                      level: '3',
                    },
                    {
                      title: '鲜花',
                      level: '3',
                    },
                  ],
                },
                {
                  title: '服饰箱包',
                  level: '2',
                  childList: [
                    {
                      title: '服饰',
                      level: '3',
                    },
                    {
                      title: '箱包',
                      level: '3',
                    },
                  ],
                },
              ],
            },
          ],
        },
        {
          type: '离我最近',
          firstList: [{ title: '离我最近',level: '1', }, { title: '智能排序',level: '1', }, { title: '精品推荐',level: '1', }],
        },
        {
          type: '筛选',
          firstList: [{ title: '筛选',level: '1', }, { title: '优惠券',level: '1', }, { title: '优惠买单',level: '1', }],
        },
      ],
最外面引用组件
<car-cascader :defaultList="defaultList" ref="cascaderItem"></car-cascader>
组件代码(下拉部分的代码)carCascader
<template>
  <div class="md-drop-menu">
    <!-- tabbar -->
    <div class="md-drop-menu-bar">
      <div
        class="bar-item"
        :class="{
          active: isPopupShow[index],
        }"
        v-for="(item, index) in tabList"
        :key="index"
        @click="tabClick(item, index)"
      >
        <span>
          {{ `${item.length > 5 ? item.slice(0, 5) + '...' : item}` }}
        </span>
      </div>
    </div>
    <!-- popup -->
    <md-popup v-model="isPopupShow[activeTabIndex]" position="top" @show="$_onListShow" @hide="$_onListHide">
      <div class="md-drop-menu-list">
        <cascaderItem :defaultList="defaultList" :activeTabIndex="activeTabIndex" @passVal="passVal" />
      </div>
    </md-popup>
  </div>
</template>
<script>
import Popup from '@mandmobile/popup';
import cascaderItem from './carCascader-item.vue';
export default {
  name: 'carCascader',
  props: {
    defaultList: {
      type: Array,
      default() {
        return [];
      },
    },
  },
  components: {
    [Popup.name]: Popup,
    cascaderItem,
  },
  data() {
    return {
      tabList: [],
      isPopupShow: [], // popup是否展示
      activeTabIndex: 0, // popup默认展示的索引
    };
  },
  mounted() {
    // console.log(this.defaultList,'defaultList')
    this.infoIsPopupShow();
    this.getTabList();
  },
  methods: {
    passVal(clickItem) {
      this.tabList = this.tabList.map((ite, ind) => (ind == this.activeTabIndex ? clickItem.title : ite));
      if (!clickItem.childList) {
        this.$set(this.isPopupShow, this.activeTabIndex, false);
      }
    },
    // 获取tabList数组
    getTabList() {
      this.defaultList.forEach((gettabItem) => {
        this.tabList.push(gettabItem.type);
      });
    },
    // tab每一项点击
    tabClick(item, index) {
      if (!this.isPopupShow[index]) {
        this.infoIsPopupShow();
        // tab的每一项添加样式
        this.$set(this.isPopupShow, index, true);
        // 对应的popup展示
        this.activeTabIndex = index;
      } else {
        this.$set(this.isPopupShow, index, false);
      }
    },
    // popup是否展示
    infoIsPopupShow() {
      this.tabList.forEach((tabItem, tabindex) => {
        this.$set(this.isPopupShow, tabindex, false); // 每一个tab都设成false
      });
    },
    // popup展示
    $_onListShow() {
      this.$_setScroller();
      this.$emit('show');
    },
    // popup隐藏
    $_onListHide() {
      this.$emit('hide');
    },
    $_setScroller() {
      const boxer = this.$el ? this.$el.querySelector('.md-popup-box') : null;
      if (boxer && boxer.clientHeight >= this.$el.clientHeight) {
        this.scroller = '.md-drop-menu-list';
      } else {
        return '';
      }
    },
  },
};
</script>
<style lang="stylus" scoped>
.md-drop-menu {
  font-family: PingFangSC-Regular;
  position: relative;
  height: drop-menu-height;
  box-sizing: border-box;
  font-size: drop-menu-font-size;
  font-weight: drop-menu-font-weight;

  .md-drop-menu-bar {
    position: relative;
    display: flex;
    height: 100%;
    font-size: 24px !important;
    font-weight: 700 !important;
    background: #F4F4F4;
    color: #443B50;
    z-index: 2000;

    .bar-item {
      display: flex;
      flex: 1;
      margin: 0;
      align-items: center;
      justify-content: center;

      span {
        position: relative;
        padding-right: 30px;

        &:after {
          content: '';
          position: absolute;
          right: 0;
          top: calc(50% - 0.07rem);
          width: 0.24rem;
          height: 0.14rem;
          background: url('./assets/images/arrow1.png') no-repeat;
          background-size: contain;
          transition: transform 0.3s ease-in-out-quint;
        }
      }

      &.active {
        color: #904EF4;

        span:after {
          transform: rotate(180deg);
        }
      }
    }
  }

  .md-popup {
    top: drop-menu-height;
    right: 0;
    bottom: 0;
    left: 0;
    position: absolute;
    height: 100vh;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    pointer-events: none;

    .md-drop-menu-list {
      width: 100%;
      max-height: 500px;
      background: drop-menu-list-bg;
      box-sizing: border-box;
      text-align: left;
      border-radius: 0 0 16px 16px;
    }
  }
}
</style>

组件当中子组件的代码,carCascader-item.vue
<template>
  <div class="cascader-list">
    <div v-for="(item, index) in newArr" :key="index" id="listBox">
      <div
        v-for="(subItem, subIndex) in item"
        :key="subIndex"
        @click="subItemClick(subItem, subIndex)"
        class="item"
        :class="[{ clickedColor: subItem.isShowChild, clickedBackground: subItem.isShowChildBack }]"
      >
        {{ subItem.title }}
      </div>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    defaultList: {
      type: Array,
      default() {
        return [];
      },
    },
    activeTabIndex: {
      type: Number,
      default: -1,
    },
  },
  data() {
    return {
      newArr: [], // 初始数据处理
      firstFloor: [], // 第一层的数据
      secondFloor: [], // 第二层的数据
      thirdFloor: [], // 第三层的数据
      isShowChild: false, // 是否展示字体颜色
      isShowChildBack: false, // 是否显示背景色
      saveSecondFloor:[], // 存储第二层数组
      saveThirdFloor:[],// 存储第三层数组
    };
  },
  watch: {
    // 监听索引,当索引改变,重新获取列表,并且将之前的列表清空
    activeTabIndex: function (newval, oldval) {
      this.activeTabIndex = newval;
      this.newArr = [];
      this.handleDefaultList();
      if(this.activeTabIndex == 1){
        this.newArr.push(this.secondFloor,this.thirdFloor)
      }
    },
  },
  mounted() {
    this.handleDefaultList();
  },
  methods: {
    // 处理初始数据
    handleDefaultList() {
      this.firstFloor = this.defaultList[this.activeTabIndex].firstList;
      this.newArr.push(this.firstFloor);
    },
    // 点击事件
    subItemClick(subItem, subIndex) {
      this.$emit('passVal',subItem)
      // 点击添加样式
      if (subItem.hasOwnProperty('isShowChild')) {
        subItem.isShowChild = true;
        subItem.isShowChildBack = true;
      } else {
        this.$set(subItem, 'isShowChild', true);
        this.$set(subItem, 'isShowChildBack', true);
      }
      // 点击同级其他同级元素样式去掉
      if (subItem.isShowChild || subItem.isShowChildBack) {
        let handleArr = [];
        // 判断等级
        if (subItem.level == 1) {
          // 如果等级是1,切掉二三级
          this.newArr.splice(1, 2);
          handleArr = this.firstFloor;
        } else if (subItem.level == 2) {
          // 如果等级是2,切掉第三级
          this.newArr.splice(2, 1);
          handleArr = this.secondFloor;
        } else {
          handleArr = this.thirdFloor;
        }
        // 同级样式去重
        handleArr.forEach((fitem, findex) => {
          const isSameColor = fitem.hasOwnProperty('isShowChild') && fitem.isShowChild && subIndex !== findex;
          const isSameBack = fitem.hasOwnProperty('isShowChildBack') && fitem.isShowChildBack && subIndex !== findex;
          if (isSameColor) {
            fitem.isShowChild = false;
          }
          if (isSameBack) {
            fitem.isShowChildBack = false;
          }
        });
      }
      // 判断等级并且判断是否有子集
      if (subItem.level == 1 && subItem.childList) {
        // 父元素下的子元素和父元素背景色相同
        subItem.childList.forEach((item) => {
          this.$set(item, 'isShowChildBack', true);
        });
        this.secondFloor = subItem.childList;
        this.newArr.push(this.secondFloor);
      }
      if (subItem.level == 2 && subItem.childList) {
        subItem.childList.forEach((item) => {
          this.$set(item, 'isShowChildBack', true);
        });
        this.thirdFloor = subItem.childList;
        this.newArr.push(this.thirdFloor);
      }
    },
  },
};
</script>
<style lang="stylus" scoped>
.cascader-list {
  overflow: hidden;
  overflow-y: scroll;
  max-height: 500px;
  display: flex;
  font-size: 24px;
  color: #655A72;
  justify-content: flex-start;

  #listBox {
    width: 100%;
  }

  .item {
    height: 100px;
    line-height: 100px;
    padding-left: 40px;
  }

  .clickedColor {
    color: #904ef4;
  }

  .clickedBackground {
    background: #f5f5f7;
  }
}
</style>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值