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


Posted in Javascript onJuly 01, 2020

   在使用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 ? '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" alt="" 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 ? '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 ? '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" alt="" 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 ? '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" alt="" 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商城中商品“筛选器”功能的实现代码的文章就介绍到这了,更多相关vue商品筛选器内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
用javascript获取textarea中的光标位置
May 06 Javascript
jQuery 1.5最新版本的改进细节分析
Jan 19 Javascript
浅谈JavaScript的事件
Feb 27 Javascript
基于cssSlidy.js插件实现响应式手机图片轮播效果
Aug 30 Javascript
js实现音频控制进度条功能
Apr 01 Javascript
jQuery实现参数自定义的文字跑马灯效果
Aug 15 jQuery
css配合JavaScript实现tab标签切换效果
Oct 11 Javascript
ES6如何用一句代码实现函数的柯里化
Jan 18 Javascript
Vue+tracking.js 实现前端人脸检测功能
Apr 16 Javascript
Vue Cli3 打包配置并自动忽略console.log语句的方法
Apr 23 Javascript
原生js实现随机点名
Jul 05 Javascript
Js类的构建与继承案例详解
Sep 15 Javascript
vue实现购物车列表
Jun 30 #Javascript
vue实现简单图片上传
Jun 30 #Javascript
vue基于better-scroll仿京东分类列表
Jun 30 #Javascript
vue使用better-scroll实现滑动以及左右联动
Jun 30 #Javascript
vue基于better-scroll实现左右联动滑动页面
Jun 30 #Javascript
Postman动态获取返回值过程详解
Jun 30 #Javascript
JS简易计算器实例讲解
Jun 30 #Javascript
You might like
Php部分常见问题总结
2006/10/09 PHP
php微信公众号开发之音乐信息
2018/10/20 PHP
laravel 中某一字段自增、自减的例子
2019/10/11 PHP
javascript引导程序
2008/10/26 Javascript
jQuery checkbox全选/取消全选实现代码
2009/11/14 Javascript
Jquery选择器 $实现原理
2009/12/02 Javascript
js 无提示关闭浏览器页面的代码
2010/03/09 Javascript
js滚动条回到顶部的代码
2011/12/06 Javascript
jquery.pagination.js 无刷新分页实现步骤分享
2012/05/23 Javascript
js 输出内容到新窗口具体实现代码
2013/05/31 Javascript
jQuery调用ajax请求的常见方法汇总
2015/03/24 Javascript
jQuery鼠标事件汇总
2015/08/30 Javascript
jQuery判断浏览器并动态调整select宽度的方法
2016/03/02 Javascript
利用JavaScript阻止表单提交的两种方法
2016/08/11 Javascript
利用jquery禁止外层滚动条的滚动
2017/01/05 Javascript
基于React+Redux的SSR实现方法
2018/07/03 Javascript
vue 判断页面是首次进入还是再次刷新的实例
2020/11/05 Javascript
[53:18]Spirit vs Liquid Supermajor小组赛A组 BO3 第三场 6.2
2018/06/03 DOTA
Python后台开发Django会话控制的实现
2019/04/15 Python
PyQt4编程之让状态栏显示信息的方法
2019/06/18 Python
实例详解Python模块decimal
2019/06/26 Python
Django REST framework 单元测试实例解析
2019/11/07 Python
Python文件读写w+和r+区别解析
2020/03/26 Python
HTML5 Plus 实现手机APP拍照或相册选择图片上传功能
2016/07/13 HTML / CSS
html2canvas把div保存图片高清图的方法示例
2018/03/05 HTML / CSS
英国太阳镜品牌:Taylor Morris Eyewear
2018/04/18 全球购物
暑期实习鉴定
2013/12/16 职场文书
师德学习感言
2014/01/31 职场文书
建筑设计专业求职自我评价
2014/03/02 职场文书
高中生旷课检讨书
2014/10/08 职场文书
个人先进事迹材料
2014/12/29 职场文书
家长给老师的感谢信
2015/01/20 职场文书
2016年寒假政治学习心得体会
2015/10/09 职场文书
工作汇报材料难写?方法都在这里了!
2019/07/01 职场文书
MySQL中的引号和反引号的区别与用法详解
2021/10/24 MySQL
python中字符串String及其常见操作指南(方法、函数)
2022/04/06 Python