二、組件設計
接下來是開發選擇框組件,首先需要自定義一個點擊外部使下拉菜單關閉的指令:
export default { bind(el, binding, vnode) { function documentHandler(e) { if (el.contains(e.target)) { return false; } if (binding.expression) { binding.value(e); } } el.__vueClickOutside__ = documentHandler; document.addEventListener('click', documentHandler); }, unbind(el, binding) { document.removeEventListener('click', el.__vueClickOutside__); delete el.__vueClickOutside__; } }
使用方法很簡單,在需要該指令的組件中:
import clickoutside from '../directives/clickoutside'
然后申明該指令:
directives: {
clickoutside
}
使用的時候就像這樣給他一個事件函數:
v-clickoutside="dosomething"
2)開發下拉框組件
首先考慮到下拉菜單的數據雙向綁定,而且分為單選和多選兩種情況,所以使用v-model來綁定數據,同時添加一個multiple的布爾類型數據:
<template> <div class="my-select"></div> </template> <script> import emitter from '../mixins/emitter' import clickoutside from '../directives/clickoutside' export default { name: 'mySelect', mixins: [emitter], directives: { clickoutside }, props: { value: { type: [String, Array] }, multiple: { type: Boolean, default: false } }, model: { prop: 'value', event: 'change' }, data() { return { focus: false, show: true } } } </script>
多選的情況下還需要引入一個tag組件:
<template> <span class="my-tag"> <slot></slot> <i class="fa fa-times-circle" v-show="closeable" @click="closeHandler"></i> </span> </template> <script> import emitter from "../mixins/emitter"; export default { name: "myTag", mixins: [emitter], props: { closeable: { type: Boolean, default: false }, closeHandler: { type: Function, default: () => () => {} } } }; </script> <style lang="scss" scoped> .my-tag { border: 1px solid black; border-radius: 2px; padding: 0 2px; > .fa-times-circle { cursor: pointer; &:hover { color: red; } } } </style>
它看起來像是這個樣子的:
需要注意的是,如果下拉菜單有需要帶默認值的情況,需要在數據加載的時候根據v-model的value值去查找對應的label文本,特別是單選的情況。這里有一個坑要注意一下,一般我們在做對應查找的時候為了方便會直接把值放到數組的對應位置,然而vue的雙向數據綁定無法捕捉到數組索引位置發生的變化,因此需要先放到一個臨時數組里,在遍歷完成的時候再賦值:
init() { if (this.multiple) { let tmpLabel = []; this.$children.forEach(child => { if (child.$options.name === "myOption") { let index = this.value.indexOf(child.value); if (index !== -1) { tmpLabel[index] = child.myLabel; } } }); this.label = tmpLabel; } else { this.$children.forEach(child => { if ( child.$options.name === "myOption" && child.value === this.value ) { this.label = child.myLabel; } }); } }
3)組裝下拉菜單
下拉框是整個組件中起到承上啟下的作用,先上進行雙向數據綁定,向下控制選項的狀態,其核心代碼如下:
this.$on("option-click", (selected, label, value) => { if (this.multiple) { if (selected) { this.broadcast("myOption", "cancel", value); let index = this.value.indexOf(value); if (index !== -1) { let tmpValue = this.value; tmpValue.splice(index, 1); this.$emit("change", tmpValue); this.label.splice(index, 1); } } else { this.broadcast("myOption", "select", value); this.$emit("change", [...this.value, value]); this.label.push(label); } } else { if (!selected) { if (this.value) { this.broadcast("myOption", "cancel", this.value); } this.broadcast("myOption", "select", value); this.$emit("change", value); this.label = label; this.show = false; } } });
最后演示一下功能,樣式是隨便寫的emmmm...
源碼下載(需要自己裝包):https://files.cnblogs.com/files/viewts/dropdown.zip