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>
<ul>
<li v-for="(item, index) in optionsDatas" :key="index">
<a @click="onOptionsItemClick(item, index)">
<span :class="{'goods-options-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
<span 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 v-show="isShowSubContent">
<ul>
<li v-for="(item, index) in selectOption.subs" :key="index" @click="onSubOptionsItemClick(item, index)">
<a>
<span :class="{'options-sub-content-list-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
<img v-show="selectOption.id === item.id" src="https://www.tuicool.com/articles/m6j22ai/@img/options-select.svg" srcset="">
</a>
</li>
</ul>
</div>
</transition>
<div v-show="isShowSubContent" @click="isShowSubContent = false"></div>
</div>

1、渲染一级菜单

<ul>
<li v-for="(item, index) in optionsDatas" :key="index">
<a @click="onOptionsItemClick(item, index)">
<span :class="{'goods-options-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
<span 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 v-show="isShowSubContent">
<ul>
<li v-for="(item, index) in selectOption.subs" :key="index" @click="onSubOptionsItemClick(item, index)">
<a>
<span :class="{'options-sub-content-list-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
<img v-show="selectOption.id === item.id" src="https://www.tuicool.com/articles/m6j22ai/@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 v-show="isShowSubContent" @click="isShowSubContent = false"></div>

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

<template>
<div>
<ul>
<li v-for="(item, index) in optionsDatas" :key="index">
<a @click="onOptionsItemClick(item, index)">
<span :class="{'goods-options-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
<span 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 v-show="isShowSubContent">
<ul>
<li v-for="(item, index) in selectOption.subs" :key="index" @click="onSubOptionsItemClick(item, index)">
<a>
<span :class="{'options-sub-content-list-item-content-name-active' : selectOption.id === item.id}">{{item.name}}</span>
<img v-show="selectOption.id === item.id" src="https://www.tuicool.com/articles/m6j22ai/@img/options-select.svg" srcset="">
</a>
</li>
</ul>
</div>
</transition>
<div 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商城中商品“筛选器”功能的实现

09 . Prometheus监控tomcat+jvm

上一篇

CSS变量对JS交互组件开发带来的提升与变革

下一篇

你也可能喜欢

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

长按储存图像,分享给朋友