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 相关文章推荐
jQuery代码优化之基本事件
Nov 01 Javascript
Javascript浅谈之this
Dec 17 Javascript
jquery教程限制文本框只能输入数字和小数点示例分享
Jan 13 Javascript
基于JavaScript如何实现私有成员的语法特征及私有成员的实现方式
Oct 28 Javascript
JQuery统计input和textarea文字输入数量(代码分享)
Dec 29 Javascript
从零开始学习Node.js系列教程之SQLite3和MongoDB用法分析
Apr 13 Javascript
BootStrap 获得轮播中的索引和当前活动的焦点对象
May 11 Javascript
深入浅析Vue中的Prop
Jun 10 Javascript
Vue项目路由刷新的实现代码
Apr 17 Javascript
vue实现歌手列表字母排序下拉滚动条侧栏排序实时更新
May 14 Javascript
Vue 组件复用多次自定义参数操作
Jul 27 Javascript
JavaScript 闭包的使用场景
Sep 17 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
判“新”函数:得到今天与明天的秒数
2006/10/09 PHP
优化使用mysql存储session的php代码
2008/01/10 PHP
PHP 解决utf-8和gb2312编码转换问题
2010/03/18 PHP
7个超级实用的PHP代码片段
2011/07/11 PHP
PHP树的深度编历生成迷宫及A*自动寻路算法实例分析
2015/03/10 PHP
php项目开发中用到的快速排序算法分析
2016/06/25 PHP
利用PHP访问带有密码的Redis方法示例
2017/02/09 PHP
PHP的HTTP客户端Guzzle简单使用方法分析
2019/10/30 PHP
php设计模式之原型模式分析【星际争霸游戏案例】
2020/03/23 PHP
索趣科技的答案
2007/02/07 Javascript
js tab 选项卡
2009/04/26 Javascript
JavaScript生成GUID的多种算法小结
2013/08/18 Javascript
使用jQuery简单实现模拟浏览器搜索功能
2014/12/21 Javascript
纯javascript实现的小游戏《Flappy Pig》实例
2015/07/27 Javascript
js原生代码实现轮播图的实例讲解
2017/07/28 Javascript
关于AngularJS中ng-repeat不更新视图的解决方法
2018/09/30 Javascript
使用React-Router实现前端路由鉴权的示例代码
2020/07/26 Javascript
vue中利用three.js实现全景图的完整示例
2020/12/07 Vue.js
js实现简单商品筛选功能
2021/02/02 Javascript
在Python的Bottle框架中使用微信API的示例
2015/04/23 Python
简述Python中的面向对象编程的概念
2015/04/27 Python
详解Django 中是否使用时区的区别
2018/06/14 Python
Python urllib3软件包的使用说明
2020/11/18 Python
利用纯css3实现的文字亮光特效的代码演示
2014/11/27 HTML / CSS
NYX Professional Makeup官方网站:专业彩妆和美容产品
2019/10/29 全球购物
年度考核自我鉴定
2013/11/09 职场文书
工作会议主持词
2014/03/17 职场文书
年会主持词结束语
2014/03/27 职场文书
市场总经理岗位职责
2014/04/11 职场文书
文明演讲稿范文
2014/05/12 职场文书
个人安全生产责任书
2014/07/28 职场文书
党的群众路线批评与自我批评发言稿
2014/10/16 职场文书
走群众路线学习笔记
2014/11/06 职场文书
幼儿园辞职书
2015/02/26 职场文书
go语言求任意类型切片的长度操作
2021/04/26 Golang
详解Laravel框架的依赖注入功能
2021/05/27 PHP