概述:Dropdown 下拉菜单与 Select 的区别在于它是包含了一部分操做,因此它的 Option 下拉项要对其进行支持。javascript
publish:2019-03-31css
自从我写了关于中后台系统组件的第一篇文章以来,已经三个月了。因为业务场景的较为简单,因此它没有其余组件库的功能那么强大,可是总体结构较为相似,代码结构清晰,容易扩展。html
将 Dropdown 分为两个模块,父组件 Dropdown,子组件 Dropdown-option。其中Dropdown 负责控制总体的显示,Drop-option 负责下拉菜单的每个选项。java
├── Dropdownide
├── Dropdown-optionpost
父组件主要负责的是选中项的显示以及下拉菜单状态(开、合)的控制。ui
具体的代码以下this
<template>
<div :class="['dropdown', { 'is-hover': trigger === 'hover' && isOpen }]" tabindex="0" @click.stop="isOpen = !isOpen" @blur="trigger !== 'hover' && (isOpen = false)" >
<div :class="['dropdown__label']">
<slot name="label">
<span class="c-color-success">{{ placeholder }}</span>
</slot>
<fat-icon name="expand_more" class="c-color-success" />
</div>
<div class="dropdown__menu" v-if="trigger === 'hover' || isOpen">
<slot name="menu"></slot>
</div>
</div>
</template>
<script> export default { name: "dropdown", provide() { return { Dropdown: this }; }, props: { placeholder: { type: String, default: "下拉菜单" }, trigger: { type: String, default: "hover" }, selectValue: { type: [String, Number] }, optionKey: { type: String, default: "value" } }, data() { return { isOpen: this.trigger === "hover", selectItem: {} }; }, model: { prop: "selectValue", event: "select" } }; 复制代码
首先对处理下拉菜单开关状态的控制,依据 trigger
也就是触发方式的不一样,能够分为两类 hover
|| click
。spa
trigger = 'hover'
时,对最外层的 div
添加 is-hover
的类名,它主要是负责添加 :hover
伪类来显示下拉菜单&.is-hover {
&:hover {
.dropdown__menu {
display: block;
}
}
.dropdown__menu {
display: none;
}
}
复制代码
同时依据 trigger
初始化 isOpen
状态为 true
。双向绑定
trigger = 'click'
时,利用 isOpen
的状态来控制下拉菜单的开、合。主要是依据事件来触发,利用 @click.stop="isOpen = !isOpen"
,来完成下来菜单的展开操做。以后,对最外层的 div
添加 tabindex="0"
属性使得它可以触发失焦事件 blur
,同时添加 @blur="trigger !== 'hover' && (isOpen = false)"
,意味着当它失效的时候,自动关闭下拉菜单。以上完成了 Dropdown 对下拉菜单控制的功能,利用 provide
,完成它与 Dropdown-option 的通信,传递 selectValue
、selectItem
、optionKey
。
Dropdown-option 是下拉菜单的每一个选项,其基本结构
<template>
<div :class="[ 'dorpdown-option', { 'is-disabled': disabled }, { 'is-selected': isSelected } ]" @mousedown="handleClick" >
<slot>
{{ label }}
</slot>
</div>
</template>
复制代码
主要是利用默认插槽,和 label
属性来构建每一项,而且包含着两种状态,是否 disabled 或 selected。disabled 状态是依据 props: disabled
来改变的,而 selected 则是由 computed
来完成的
<script>
export default {
inject: {
Dropdown: { default: "Dropdown" }
},
computed: {
isSelected() {
const {
Dropdown: { optionKey, selectValue }
} = this;
const key = this[optionKey] || this.$attrs[optionKey];
return key === selectValue;
}
},
...
};
</script>
复制代码
首先利用 inject
将父组件 Dropdown 注入,这样能够经过 this.Dropdown
来访问它的状态、属性。
而后在 isSelected()
中获取 selectValue
,与当前 Dropdown-option 的 key
值进行比对,查看是否为选中项。
为了要引入 optionKey,是由于在实际的业务中,有的场景会以 label 做为去区分项,有的则是以 value,故引入,方便自定义。
每一个 Dropdown-option 具有选中功能,可是从 @mousedown="handleClick"
能够看出,利用 mousedown 来代替 click 事件
因为咱们利用 Dropdown 的 blur 事件来控制下拉列表的展开与关闭,此时若是利用 click 事件,则会在 blur 以后触发,因此没法选中。故采用 mousedown 来完成该功能。
methods: {
handleSelect(key) {
let {
Dropdown: { multiple, trigger },
value,
label
} = this;
this.Dropdown.$emit("change", key);
this.Dropdown.$emit("select", key);
if (trigger !== "hover") {
this.Dropdown.isOpen = false;
}
},
handleClick() {
let {
Dropdown: { optionKey },
disabled
} = this;
const key = this[optionKey] || this.$attrs[optionKey];
if (!disabled) {
this.$slots.default[0].elm.click && this.$slots.default[0].elm.click();
key && this.handleSelect(key);
}
}
}
复制代码
这一份部分的逻辑就比较简单了,只有一处须要解释下
this.$slots.default[0].elm.click && this.$slots.default[0].elm.click();
复制代码
因为咱们利用 mousedown 来代替原来的 click 事件,但咱们利用 slot
插槽来完成下拉菜单的开发时,就没法触发 slot
的点击时间,因此利用上述代码来手动触发。
因为 Dropdown 组件中,使用了 v-model
来完成数据的双向绑定
model: {
prop: "selectValue",
event: "select"
}
复制代码
因此在 Dropdown-option 中则须要利用 this.Dropdown.$emit("select", key);
来完成双向绑定。
代码地址:Dropdown Github
实例:Fat-UI lib