Dropdownitem组件完整代码
wxml
html
<!-- 展示该menu对应的选项,控制选中逻辑,通知父组件调用更新方法 -->
<view class="fty_dropdown_item">
<!-- 遮罩层 -->
<view wx:if="{{overlay}}"
class="cover" style="{{showDropdown?'height:100vh;opacity:1;':''}}"
bind:tap="clickOverlayClose">
</view>
<!-- 选项列表 -->
<!-- 这里需要动态计算展开后的高度,否则动画上会出现意料之外的效果 -->
<scroll-view
scroll-y
enable-flex
class="dropdown_options custom_options_class {{showDropdown?'show':'hide'}}"
style="height: {{scrollViewHeight}}rpx;"
>
<view
class="dropdown_option custom_option_class {{item.value === value ? 'active' : ''}}"
style="{{ item.value === value ? 'color:' + activeColor : '' }}"
wx:for="{{ options }}" wx:key="value"
bind:tap="selectOption" data-value="{{ item.value }}"
>
{{ item.text }}
</view>
</scroll-view>
</view>
js
js
const relationPath = '../fty-dropdownmenu/fty-dropdownmenu';
Component({
relations: {
[relationPath]: {
type: 'parent', // 关联的目标节点应为父节点
linked: function (target) {
// 每次被插入到custom-ul时执行,target是custom-ul节点实例对象,触发在attached生命周期之后
},
linkChanged: function (target) {
// 每次被移动后执行,target是custom-ul节点实例对象,触发在moved生命周期之后
},
unlinked: function (target) {
// 每次被移除时执行,target是custom-ul节点实例对象,触发在detached生命周期之后
}
}
},
options: {
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
/**
* 组件:<custom-component class="my-class"></custom-component>
* 页面:<custom-component my-class="red-text" />
*/
externalClasses: ['custom_options_class', 'custom_option_class'],
properties: {
value: {
type: [String, Number],
optionalTypes: [null, undefined],
value: '',
},
options: {
type: Array,
value: [],
observer(newV, oldV) {
// this.updateOptionText(this.properties.value);
}
},
activeColor: {
type: String,
value: '#000000'
}
},
data: {
triggerText: '',
showDropdown: false,
scrollViewHeight: 0,
overlay: true,
},
lifetimes: {
attached() {
/**
* 如果options中默认有一个选项,那么可以这里可以直接调用 updateOptionText;
* 如果options中所有选项都是异步获取,则这里不再调用 updateOptionText,而是在 options.observer 中调用。
*/
this.updateOptionText(this.properties.value);
},
ready() {
// 获取父组件实例
Object.defineProperty(this, 'parent', {
get: () => this.getRelationNodes(relationPath)[0],
});
// 是否需要overlay
this.setData({
overlay: this.parent?.data?.overlay || false
})
},
},
methods: {
/**
* 触发父组件更新数据
*/
rerender() {
wx.nextTick(() => {
this.parent?.updateItemListData();
});
},
/**
* 更新选中后展示的选项文字,并通知父组件更新menu展示项的title
* @param {*} value
*/
updateOptionText(value) {
this.setData({
triggerText: this.getOptionText(value)
}, () => {
this.rerender()
});
},
/**
* 切换“展开”、“关闭”
*/
toggleDropdown(cb) {
if (this.data.showDropdown) {
this.close(cb);
} else {
this.open(cb)
}
},
open(cb) {
this.setData({
showDropdown: true
}, () => {
this._updateScrollViewHeight();
cb && cb(this.data.showDropdown)
});
},
close(cb) {
this.setData({
showDropdown: false
}, () => {
cb && cb(this.data.showDropdown)
});
},
clickOverlayClose() {
this.close();
this.rerender();
},
selectOption(event) {
const {
value
} = event.currentTarget.dataset;
this.triggerEvent('onchange', {
value
}, {})
this.setData({
value,
showDropdown: false
});
this.updateOptionText(value);
},
/**
* 更新根据value选中项后,展示的title文字
* @param {*} value
*/
getOptionText(value) {
const option = this.properties.options.find(item => item.value === value);
return option ? option.text : '';
},
_updateScrollViewHeight() {
let len = this.properties.options.length;
let scrollViewHeight = this.data.showDropdown ? Math.ceil(len / 3) * (72 + 30 * 2) : 0;
this.setData({
scrollViewHeight
})
}
}
})
wxss
css
.fty_dropdown_item {
position: relative;
width: 100%;
}
.dropdown_options {
border-top: solid 1px #f5f5f5;
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 1001;
background-color: #fff;
padding: 0 30rpx;
float: left;
transition: all .2s ease-out;
box-sizing: border-box;
}
.show {
padding: 30rpx;
/* height: xxx; 实时计算 */
max-height: 48vh;
overflow-y: scroll;
}
.hide {
height: 0;
max-height: 0;
padding: 0 30rpx;
overflow-y: hidden;
}
.dropdown_option {
display: inline-block;
padding: 4px 0;
width: 30%;
background-color: #fff;
color: #53585F;
font-size: 26rpx;
border-radius: 10rpx;
text-align: center;
padding: 16rpx;
margin-right: 20rpx;
margin-bottom: 20rpx;
box-sizing: border-box;
transition: all .3s ease-out;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border: solid 1px #f4f4f4;
}
.active {
background-color: #f5d000;
}
.cover {
width: 100vw;
position: fixed;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
opacity: 0;
transition: opacity .2s;
}