Skip to content

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;
}

分析记录

Released under the MIT License.