概述:Dropdown 下拉選單與 Select 的區別在於它是包含了一部分操作,所以它的 Option 下拉項要對其進行支援。
publish:2019-03-31
自從我寫了關於中後臺系統元件的第一篇文章以來,已經三個月了。由於業務場景的較為簡單,所以它沒有其他元件庫的功能那麼強大,但是整體結構較為類似,程式碼結構清晰,容易擴充套件。
這次是最後一個元件了,之後會整體修改下樣式以及類名,定義下開發規範,希望可以有小夥伴一起來 feature。
將 Dropdown 分為兩個模組,父元件 Dropdown,子元件 Dropdown-option。其中Dropdown 負責控制整體的顯示,Drop-option 負責下拉選單的每一個選項。
Dropdown - 元件結構
├── Dropdown
├── Dropdown-option
Dropdown
父元件主要負責的是選中項的顯示以及下拉選單狀態(開、合)的控制。
具體的程式碼如下
<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
。
- 當
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 是下拉選單的每個選項,其基本結構
<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