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 相关文章推荐
JS 判断undefined的实现代码
Nov 26 Javascript
通过Javascript将数据导出到外部Excel文档的函数代码
Jun 15 Javascript
ReactNative-JS 调用原生方法实例代码
Oct 08 Javascript
详解js的事件处理函数和动态创建html标记方法
Dec 16 Javascript
一个炫酷的Bootstrap导航菜单
Dec 28 Javascript
jQuery命名空间与闭包用法示例
Jan 12 Javascript
vue实现ToDoList简单实例
Feb 07 Javascript
原生JS实现层叠轮播图
May 17 Javascript
Cpage.js给组件绑定事件的实现代码
Aug 31 Javascript
通过js随机函数Math.random实现乱序
May 19 Javascript
Threejs实现滴滴官网首页地球动画功能
Jul 13 Javascript
vue设置默认首页的操作
Aug 12 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中实现Javascript的escape()函数代码
2010/08/08 PHP
php代码中使用换行及(\n或\r\n和br)的应用
2013/02/02 PHP
php 字符串中的\n换行符无效、不能换行的解决方法
2014/04/02 PHP
PHP使用GETDATE获取当前日期时间作为一个关联数组的方法
2015/03/19 PHP
PHP测试成功的邮件发送案例
2015/10/26 PHP
PHP支付系统设计与典型案例分享
2016/08/02 PHP
PHP使用mysqli操作MySQL数据库的简单方法
2017/02/04 PHP
PHP后台实现微信小程序登录
2018/08/03 PHP
javascript 控制 html元素 显示/隐藏实现代码
2009/09/01 Javascript
js 关于=+与+=日期函数使用说明(赋值运算符)
2011/11/15 Javascript
JavaScript 代码压缩工具小结
2012/02/27 Javascript
JS对象与json字符串格式转换实例
2014/10/28 Javascript
推荐6款基于jQuery实现图片效果插件
2014/12/07 Javascript
javascript实现uploadify上传格式以及个数限制
2015/11/23 Javascript
利用js获取下拉框中所选的值
2016/12/01 Javascript
bootstrap 下拉多选框进行多选传值问题代码分析
2017/02/14 Javascript
使用Angular9和TypeScript开发RPG游戏的方法
2020/03/25 Javascript
taro 实现购物车逻辑的实例代码
2020/06/05 Javascript
关于angular引入ng-zorro的问题浅析
2020/09/09 Javascript
[01:05:12]2014 DOTA2国际邀请赛中国区预选赛 TongFu VS CIS-GAME
2014/05/21 DOTA
Python的加密模块md5、sha、crypt使用实例
2014/09/28 Python
Python找出最小的K个数实例代码
2018/01/04 Python
pandas 实现将重复表格去重,并重新转换为表格的方法
2018/04/18 Python
如何使用Python 打印各种三角形
2019/06/28 Python
Python并发concurrent.futures和asyncio实例
2020/05/04 Python
新版Pycharm中Matplotlib不会弹出独立的显示窗口的问题
2020/06/02 Python
Python3爬虫中pyspider的安装步骤
2020/07/29 Python
python脚本第一行如何写
2020/08/30 Python
服务承诺书范文
2014/05/19 职场文书
活动总结报告怎么写
2014/07/03 职场文书
求职导师推荐信范文
2015/03/27 职场文书
医院党建工作总结2015
2015/05/26 职场文书
导师鉴定意见
2015/06/05 职场文书
会议营销主持词
2015/07/03 职场文书
2019职场单身人才调研报告:互联网行业单身比例最高
2019/08/07 职场文书
Windows10安装Apache2.4的方法步骤
2022/06/25 Servers