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 自定义事件初探
Aug 21 Javascript
jquery tablesorter.js 支持中文表格排序改进
Dec 09 Javascript
Javascript中的高阶函数介绍
Mar 15 Javascript
JavaScript中的函数声明和函数表达式区别浅析
Mar 27 Javascript
Angular2学习笔记——详解路由器模型(Router)
Dec 02 Javascript
bootstrap datetimepicker实现秒钟选择下拉框
Jan 05 Javascript
JavaScript计时器用法分析【setTimeout和clearTimeout】
Jan 18 Javascript
vue2利用Bus.js如何实现非父子组件通信详解
Aug 25 Javascript
详解ionic本地相册、拍照、裁剪、上传(单图完全版)
Oct 10 Javascript
微信小程序动画(Animation)的实现及执行步骤
Oct 28 Javascript
深入解析koa之中间件流程控制
Jun 17 Javascript
解决await在forEach中不起作用的问题
Feb 25 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 define()函数及defined()函数使用详解
2013/06/09 PHP
PHP Cookie学习笔记
2016/08/23 PHP
PHP单例模式应用示例【多次连接数据库只实例化一次】
2018/12/18 PHP
php中isset与empty函数的困惑与用法分析
2019/07/05 PHP
php实现推荐功能的简单实例
2019/09/29 PHP
来自chinaz的ajax获取评论代码
2008/05/03 Javascript
javascript ajax 仿百度分页函数
2013/10/29 Javascript
js使用DOM操作实现简单留言板的方法
2015/04/10 Javascript
javascript递归回溯法解八皇后问题
2015/04/22 Javascript
jQuery checkbox选中问题之prop与attr注意点分析
2016/11/15 Javascript
Javascript前端经典的面试题及答案
2017/03/14 Javascript
安装vue-cli报错 -4058 的解决方法
2017/10/19 Javascript
js实现以最简单的方式将数组元素添加到对象中的方法
2017/12/20 Javascript
angularjs select 赋值 ng-options配置方法
2018/02/28 Javascript
微信小程序登录换取token的教程
2018/05/31 Javascript
vue+echarts实现动态绘制图表及异步加载数据的方法
2018/10/17 Javascript
微信小程序之下拉列表实现方法解析(附完整源码)
2019/08/23 Javascript
基于Vue的商品主图放大镜方案详解
2019/09/19 Javascript
[01:58]DOTA2上海特级锦标赛现场采访:RTZ这个ID到底好不好
2016/03/25 DOTA
[01:10]为家乡而战!完美世界城市挑战赛全国总决赛花絮
2019/07/25 DOTA
Python 深入理解yield
2008/09/06 Python
在Pycharm中使用GitHub的方法步骤
2019/06/13 Python
python爬虫中多线程的使用详解
2019/09/23 Python
Win系统PyQt5安装和使用教程
2019/12/25 Python
Macbook安装Python最新版本、GUI开发环境、图像处理、视频处理环境详解
2020/02/17 Python
纯css3(无图片/js)制作的几个社交媒体网站的图标
2013/03/21 HTML / CSS
html5构建触屏网站之touch事件介绍
2013/01/07 HTML / CSS
ivx平台开发之不用代码实现一个九宫格抽奖功能
2021/01/27 HTML / CSS
英国女性运动服品牌:Sweaty Betty
2018/11/08 全球购物
学院领导推荐信
2013/10/30 职场文书
大学生社会实践活动总结
2014/07/03 职场文书
2014年中秋节活动总结
2014/08/29 职场文书
2015年党性分析材料
2014/12/19 职场文书
队名及霸气口号大全
2015/12/25 职场文书
apache基于端口创建虚拟主机的示例
2021/04/24 Servers
JavaScript与JQuery框架基础入门教程
2021/07/15 Javascript