概述:Dropdown 下拉菜单与 Select 的区别在于它是包含了一部分操作,所以它的 Option 下拉项要对其进行支持。
publish:2019-03-31
自从我写了关于中后台系统组件的以来,已经三个月了。由于业务场景的较为简单,所以它没有其他组件库的功能那么强大,但是整体结构较为类似,代码结构清晰,容易扩展。
将 Dropdown 分为两个模块,父组件 Dropdown,子组件 Dropdown-option。其中Dropdown 负责控制整体的显示,Drop-option 负责下拉菜单的每一个选项。
Dropdown - 组件结构
├── Dropdown
├── Dropdown-option
Dropdown
父组件主要负责的是选中项的显示以及下拉菜单状态(开、合)的控制。
具体的代码如下
{ { placeholder }}
首先对处理下拉菜单开关状态的控制,依据 trigger
也就是触发方式的不同,可以分为两类 hover
|| click
。
- 当
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
Dropdown-option 是下拉菜单的每个选项,其基本结构
复制代码{ { label }}
主要是利用默认插槽,和 label
属性来构建每一项,并且包含着两种状态,是否 disabled 或 selected。disabled 状态是依据 props: disabled
来改变的,而 selected 则是由 computed
来完成的
复制代码
首先利用 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);
来完成双向绑定。
总结
代码地址:
实例: