温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

vue如何实现商城中商品“筛选器”功能

发布时间:2020-07-01 11:04:10 来源:亿速云 阅读:372 作者:清晨 栏目:开发技术

小编给大家分享一下vue如何实现商城中商品“筛选器”功能,希望大家阅读完这篇文章后大所收获,下面让我们一起去探讨方法吧!

vue商城中商品“筛选器”功能的实现

   在使用vue搭建商城项目的时候,要实现一个商品筛选器的功能,在完成之后,再一次被vue的数据驱动的强大感到震撼!

       首先,我们来看一下具体的需求吧。你可以先看下面的这两张图,然后再看文字描述,可能会更容易理解。

vue如何实现商城中商品“筛选器”功能

没有触发时的状态

vue如何实现商城中商品“筛选器”功能

触发后的状态

       我们需求有下面几点:
       1、默认情况下,只显示一级菜单,二级菜单不显
       2、存在二级菜单的情况下,在二级菜单没有显示的情况下,点击一级菜单,一级菜单的样式发生改变,二级菜单不显示
       3、存在二级菜单的情况下,一级菜单已经点击过之后,再点击一级菜单,会显示二级菜单
       我们举例子说明一下,当前的一级菜单有默认、有货优先、直营优先,只有默认是含有二级菜单的,比如现在焦点在有货优先上面,那么我们点击默认的时候,不会弹出默认下面的二级菜单,只会改变一级菜单默认的样式(字体和三角形的颜色),当再次点击一级菜单默认的时候,其下面的二级菜单就显示出来了。
       需求分析完成后,我们开始编写代码吧。

一、创建筛选器数据结构

       跟以前的开发方式不同,我们首先要创建数据结构,而不是编写模版代码。

1、设置筛选器数据结构

// 数据源
optionsDatas: [
  {
   id: '1',
   name: '默认',
   subs: [
    {
     id: '1',
     name: '默认',
    },
    {
      id: '1-2',
      name: '价格由高到低',
    },
    {
      id: '1-3',
      name: '销量由高到低',
    },
    ]
  },
  {
   id: '2',
   name: '有货优先',
   subs: []
  },
  {
   id: '3',
   name: '直营优先',
   subs: []
  }
]

       这个数据结构设计得是非常出彩的,此处您可能还看不到,在下面具体的应用中你就能感觉到它的优美呢。

2、设置二级菜单(选中项subs)的数据结构

// 选中的筛选项
selectOption: {},
// 是否展开子筛选项
sShowSubContent: false
  当然,我们要在created钩子函数中对selecOption进行赋值操作,保证其具有初始值。

created: function () {
  // 设置初始选中项
  this.selectOption = this.optionsDatas[0];
}

二、设置模版代码

       下面是完整模版代码,内容相对比较多,我们按照功能逐块进行讲解吧。

<div class="goods-options z-index-2">
  <ul class="goods-options-list">
   <li class="goods-options-item" v-for="(item, index) in optionsDatas" :key="index">
    <a class="goods-options-item-content" @click="onOptionsItemClick(item, index)">
     <span class="goods-options-item-content-name" :class="{'goods-options-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
     <span class="goods-options-item-content-caret caret" v-if="item.subs.length > 0"
     :class="[isShowSubContent && selectOption.id === item.id &#63; 'goods-options-item-content-caret-open' : 'goods-options-item-content-caret-close']" 
     ></span>
    </a>
   </li>
  </ul>
  <transition name="fold-height">
   <div class="options-sub-content z-index-2" v-show="isShowSubContent">
    <ul class="options-sub-content-list">
     <li class="options-sub-content-list-item" v-for="(item, index) in selectOption.subs" :key="index" @click="onSubOptionsItemClick(item, index)">
      <a class="options-sub-content-list-item-content">
       <span class="options-sub-content-list-item-content-name" :class="{'options-sub-content-list-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
       <img class="options-sub-content-list-item-content-select" v-show="selectOption.id === item.id" src="@img/options-select.svg"  srcset="">
      </a>
     </li>
    </ul>
   </div>
  </transition>

  <div class="cover" v-show="isShowSubContent" @click="isShowSubContent = false"></div>
</div>
1、渲染一级菜单
  <ul class="goods-options-list">
   <li class="goods-options-item" v-for="(item, index) in optionsDatas" :key="index">
    <a class="goods-options-item-content" @click="onOptionsItemClick(item, index)">
     <span class="goods-options-item-content-name" :class="{'goods-options-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
     <span class="goods-options-item-content-caret caret" v-if="item.subs.length > 0"
     :class="[isShowSubContent && selectOption.id === item.id &#63; 'goods-options-item-content-caret-open' : 'goods-options-item-content-caret-close']" 
     ></span>
    </a>
   </li>
  </ul>

1.1、一级菜单的样式变化

       一级菜单的文字颜色的变化需要满足下面的规则,也就是selectOption.id === item.id。也就是说在当选中是一级菜单是默认的时候,我们就要其文字颜色改编成红色。

:class="{'goods-options-item-content-name-active' : selectOption.id === item.id}"

       相应地,三角形的颜色和箭头的朝向也需要进行更改。更改的逻辑如下。当然,如果一级菜单没有对应的二级菜单时,三角形就不应该显示。

:class="[isShowSubContent && selectOption.id === item.id &#63; 'goods-options-item-content-caret-open' : 'goods-options-item-content-caret-close']"
v-if="item.subs.length > 0"

1.2、一级菜单的点击事件onOptionsItemClick(item, index)实现的主要功能是改变一次菜单的样式和二级菜单的显示/隐藏。具体的功能如下分析所示:
       1、如果子选项视图处于展开状态,则关闭掉子选项视图
       2、展示子选项视图
              2.1、选中项包含子选项
              2.2、当前筛选项处于选中状态
       3、设置选中项为用户点击的选项

onOptionsItemClick: function (item, index) {
  // 如果子选项视图处于展开状态,则关闭掉子选项视图
  if (this.isShowSubContent) {
   this.isShowSubContent = false;
   return;
  }
  // 1、选中项包含子选项
  // 2、当前筛选项处于选中状态
  // 展示子选项视图
  if (item.subs.length > 0 && this.selectOption.id === item.id) {
   this.isShowSubContent = true;
  } 
   // 设置选中项为用户点击的选项
  this.selectOption = item;
}

2、渲染二级菜单

<transition name="fold-height">
 <div class="options-sub-content z-index-2" v-show="isShowSubContent">
   <ul class="options-sub-content-list">
    <li class="options-sub-content-list-item" v-for="(item, index) in selectOption.subs" :key="index" @click="onSubOptionsItemClick(item, index)">
     <a class="options-sub-content-list-item-content">
      <span class="options-sub-content-list-item-content-name" :class="{'options-sub-content-list-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
      <img class="options-sub-content-list-item-content-select" v-show="selectOption.id === item.id" src="@img/options-select.svg"  srcset="">
     </a>
    </li>
   </ul>
  </div>
</transition>

2.1、二级菜单样式的变化
       二级菜单的样式变化需要满足下面的规则。这个规则基本上跟一级菜单的一致。

:class="{'options-sub-content-list-item-content-name-active' : selectOption.id === item.id}"

       对于右侧的对勾,需要符合下面的逻辑。

v-show="selectOption.id === item.id"

2.2、二级菜单的点击事件onSubOptionsItemClick(item, index),这个事件需要实现功能如下:
       1、设置选中项为用户点击的选项
       2、将选中项置顶
       3、关闭子选项视图

onSubOptionsItemClick: function (subItem, index) { 
  // 遍历所有的可选项,将选中项置顶
  this.optionsDatas.forEach(options => {
   options.subs.forEach (subOptions => {
    if (subOptions.id === subItem.id) {
     options.id = subOptions.id;
     options.name = subOptions.name;
     }
   })
  });
  // 关闭子选项视图
  this.isShowSubContent = false;
}

2.3、二级菜单动画的实现
       二级菜单动画的实现,我们采用了vue的过度动画。其使用到的css动画如下:

/**
 子选项内容区展开动画,当 v-if=“true” 的时候调用
 当子选项部分展开时,初始状态max-height为0,结束状态max-height为180
*/
 .fold-height-enter-active {
  animation-duration: .3s;
  animation-name: fold-height-open;
 }

 @keyframes fold-height-open {
   0% {
    max-height: 0;
   }
   100% {
    max-height: px2rem(180);
   }
 }
/**
 子选项内容区关闭动画,当 v-if=false 的时候调用
 当子选项部分关闭时,初始状态max-height为180,结束状态max-height为0
*/
  .fold-height-leave-active {
   animation-duration: .3s;
   animation-name: fold-height-close;
  }

  @keyframes fold-height-close {
   0% {
    max-height: px2rem(180);
   }
   100% {
    max-height: 0;
   }
  }

2、遮罩的显示/隐藏

       最后就剩下一个遮罩的样式和逻辑了,这个比较简单,其逻辑如下:此处不在进行多余的解释。

<div class="cover" v-show="isShowSubContent" @click="isShowSubContent = false">
</div>

       至此,我们所有的逻辑分析和代码实现都已完成。设计的最巧妙的就是这个数据结构,完全满足了我们业务需求。在下面是完整的代码,希望对您有用。

<template>
 <div class="goods-options z-index-2">
  <ul class="goods-options-list">
   <li class="goods-options-item" v-for="(item, index) in optionsDatas" :key="index">
    <a class="goods-options-item-content" @click="onOptionsItemClick(item, index)">
     <span class="goods-options-item-content-name" :class="{'goods-options-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
     <span class="goods-options-item-content-caret caret" v-if="item.subs.length > 0"
     :class="[isShowSubContent && selectOption.id === item.id &#63; 'goods-options-item-content-caret-open' : 'goods-options-item-content-caret-close']" 
     ></span>
    </a>
   </li>
  </ul>
  <transition name="fold-height">
   <div class="options-sub-content z-index-2" v-show="isShowSubContent">
    <ul class="options-sub-content-list">
     <li class="options-sub-content-list-item" v-for="(item, index) in selectOption.subs" :key="index" @click="onSubOptionsItemClick(item, index)">
      <a class="options-sub-content-list-item-content">
       <span class="options-sub-content-list-item-content-name" :class="{'options-sub-content-list-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
       <img class="options-sub-content-list-item-content-select" v-show="selectOption.id === item.id" src="@img/options-select.svg"  srcset="">
      </a>
     </li>
    </ul>
   </div>
  </transition>

  <div class="cover" v-show="isShowSubContent" @click="isShowSubContent = false"></div>
 </div>
</template>


<script>
export default {
 data: function () {
  return {
   // 数据源
   optionsDatas: [
    {
     id: '1',
     name: '默认',
     subs: [
      {
       id: '1',
       name: '默认',
      },
      {
       id: '1-2',
       name: '价格由高到低',
      },
      {
       id: '1-3',
       name: '销量由高到低',
      },
     ]
    },
    {
     id: '2',
     name: '有货优先',
     subs: []
    },{
     id: '3',
     name: '直营优先',
     subs: []
    }
   ],
   // 选中的筛选项
   selectOption: {},
   // 是否展开子筛选项
   isShowSubContent: false
  }
 },
 created: function () {
  // 设置初始选中项
  this.selectOption = this.optionsDatas[0];
 },
 methods: {
  /**
   * 1、如果子选项视图处于展开状态,则关闭掉子选项视图
   * 2、展示子选项视图
   *  1、选中项包含子选项
   *  2、当前筛选项处于选中状态
   * 3、设置选中项为用户点击的选项
   */
  onOptionsItemClick: function (item, index) {
   // 如果子选项视图处于展开状态,则关闭掉子选项视图
   if (this.isShowSubContent) {
    this.isShowSubContent = false;
    return;
   }
   // 1、选中项包含子选项
   // 2、当前筛选项处于选中状态
   // 展示子选项视图
   if (item.subs.length > 0 && this.selectOption.id === item.id) {
    this.isShowSubContent = true;
   } 
   // 设置选中项为用户点击的选项
   this.selectOption = item;

   
  },
  /**
   * 1、设置选中项为用户点击的选项
   * 2、将选中项置顶
   * 3、关闭子选项视图
   */
  onSubOptionsItemClick: function (subItem, index) {
   // 设置选中项为用户点击的选项
   // this.selectOption = subItem;
   
   // 遍历所有的可选项,将选中项置顶
   this.optionsDatas.forEach(options => {
    options.subs.forEach (subOptions => {
     if (subOptions.id === subItem.id) {
      options.id = subOptions.id;
      options.name = subOptions.name;
     }
    })
   });

   // 关闭子选项视图
   this.isShowSubContent = false;
  },

 },
 watch: {
  /**
   * 当选择项发生变化的时候,需要通知父组件
   */
  selectOption: function (newValue, oldValue) {
   this.$emit('optionsChange', newValue);
  }
 }
}
</script>


<style lang="scss" scoped>
@import '@css/style.scss';
 .goods-options {
  width: 100%;
  border-bottom: 1px solid $lineColor;
  &-list {
   display: flex;
   width: 100%;
   height: $goodsOptionsHeight;
   background-color: white;
   .goods-options-item {
    flex-grow: 1;

    &-content {
     height: 100%; 
     display: flex;
     justify-content: center;
     align-items: center;

     &-name {
      font-size: $infoSize;
      margin-right: $marginSize;

      &-active{
       color: $mainColor;
      }
     }

     // 子选项展开时,三角形的动画
     &-caret {
      &-open {
       transform:rotate(-180deg);
       transition: all .3s;
      }

      &-close {
       transform:rotate(0deg);
       transition: all .3s;
      }
     }

    }
   }

  }

  // 子选项内容区
  .options-sub-content {
   // 脱离标准文档流
   position: absolute;
   width: 100%;
   max-height: px2rem(180);
   overflow: hidden;
   overflow-y: auto;
   background-color: white;
   &-list {

    &-item {

     &-content {
      display: flex;
      align-items: center;
      border-top: 1px solid $lineColor;
      padding: $marginSize;
      height: px2rem(44);
      box-sizing: border-box;
      &-name {
       font-size: $infoSize;
       display: inline-block;
       flex-grow: 1;

       &-active{
        color: $mainColor;
       }
      }

      &-select {
       width: px2rem(18);
       height: px2rem(18);
      }

     }

    }
   }
  }

  /**
   子选项内容区展开动画,当 v-if=“true” 的时候调用
   当子选项部分展开时,初始状态max-height为0,结束状态max-height为180
  */
  .fold-height-enter-active {
   animation-duration: .3s;
   animation-name: fold-height-open;
  }

  @keyframes fold-height-open {
   0% {
    max-height: 0;
   }
   100% {
    max-height: px2rem(180);
   }
  }

  /**
   子选项内容区关闭动画,当 v-if=false 的时候调用
   当子选项部分关闭时,初始状态max-height为180,结束状态max-height为0
  */
  .fold-height-leave-active {
   animation-duration: .3s;
   animation-name: fold-height-close;
  }

  @keyframes fold-height-close {
   0% {
    max-height: px2rem(180);
   }
   100% {
    max-height: 0;
   }
  }
 }
</style>

看完了这篇文章,相信你对vue如何实现商城中商品“筛选器”功能有了一定的了解,想了解更多相关知识,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI