微信小程序自定义Picker -- 右侧滑出多选框

时间:2024-04-02 19:51:59

微信小程序自定义Picker -- 右侧滑出选择框(支持多选)

效果

微信小程序自定义Picker -- 右侧滑出多选框

需求

在项目实际开发中,微信小程序的picker组件满足不了业务需求并且存在一些弊端
例如:

  1. 实际开发时,需要在picker选择列表中的每一项加图标或者图片(用于展示不同状态);
  2. 微信小程序的picker组件在iosandroid上的展示效果不同;
  3. 微信小程序的picker组件在android上的列表有无限循环的问题,而且反馈之后迟迟没有修复;
  4. 微信小程序的picker组件没有良好的多选模式(每次选择完后需要在点开选择,且在列表中没有明确的以选择标识);

说明

  1. 分装组件已完成,传参 multiple=true 为多选,multiple=false 为单选
  2. <cover-view> 标签的原因是项目中可能会有 echartstextarea 等等元素的层级影响,为了一劳永逸;
  3. js文件中的 chooseList 是选择列表,可自行动态传入,已经做了列表超出后的滑动显示;
  4. 选择完成后的 bind:chooseEvent 用于确定事件绑定,输出的是 chooseListvalue 值,根据需求使用;
  5. 实现原理其实不是很复杂,只不过从无到有也花了半天时间,不喜勿喷,转载请注明出处;
  6. 如有好的建议(例如滑动进入、退出的实现方式)或者技术讨论欢迎和本人联系;

代码

wxml

<view class='showPicker' bindtap='showPicker'>MyPicker</view>

<cover-view class='{{ showPicker ? ".shade-container" : "hide-container" }}' wx:if='{{ firstShow }}'>
	<cover-view class='left-shade' bindtap='hidePicker'></cover-view>
	<cover-view class='right-choose'>
		<cover-view class='button-container'>
			<cover-view class='cancal' bindtap='cancal'>取消</cover-view>
			<cover-view class='sure' bindtap='sure'>确定</cover-view>
		</cover-view>	
		<cover-view class='picker-container'>
			<cover-view
				class='picker-item {{ item.flag ? "picker-item-choose" : "" }}'
				wx:for='{{ chooseList }}'
				wx:key='index' wx:for-item='item'
				bindtap='chooseItem'
				data-value='{{ item.value }}'
			>
				{{ item.name }}
			</cover-view>
		</cover-view>
	</cover-view>
</cover-view>

组件js

let list = [];

Component({
	data: {
		showPicker: false,
		firstShow: false,
		list: []
	},
	properties: {
		chooseList: {
			type: Array
		},
		multiple: {
			type: Boolean
		}
	},
	methods: {
		// 点击picker元素事件	
		chooseItem(e) {
			if (this.properties.multiple) {
				// 多选事件
				let val = e.target.dataset.value;
				let arr = this.data.chooseList;
				let flag = '';
				let index = null;
				for (let i = 0, len = arr.length; i < len; i++) {
					if (arr[i].value == val) {
						index = i;
						flag = `chooseList[${i}].flag`
					}
				}
				if (!this.data.chooseList[index].flag) {
					this.setData({
						[flag]: true
					})
				} else {
					this.setData({
						[flag]: false
					})
				}
			} else {
				// 单选事件
				let val = e.target.dataset.value;
				let arr = this.data.chooseList;
				let flag = '';
				let index = null;
				for (let i = 0, len = arr.length; i < len; i++) {
					index = i;
					flag = `chooseList[${i}].flag`;
					if (arr[i].value == val) {
						this.setData({
							[flag]: true
						})
					} else {
						this.setData({
							[flag]: false
						})
					}
				}
			}
		},
		// 展示picker
		showPicker() {
			if (!this.data.firstShow) {
				this.setData({
					firstShow: true
				})
			}
			this.setData({
				showPicker: true,
			})
			// 加载时重新渲染已选择元素
			let arr = this.data.chooseList;
			let array = this.data.list;
			let flag = '';
			let index = null;
			for (let i = 0, len = arr.length; i < len; i++) {
				index = i;
				flag = `chooseList[${i}].flag`;
				if (!array.includes(arr[i].value)) {
					this.setData({
						[flag]: false
					})
				} else {
					this.setData({
						[flag]: true
					})
				}
			}
		},
		// 隐藏picker
		hidePicker() {
			this.setData({
				showPicker: false
			})
		},
		// 取消按钮事件
		cancal() {
			this.hidePicker();
		},
		// 确定按钮事件
		sure() {
			list = [];
			for (let item of this.data.chooseList) {
				if (item.flag) {
					list.push(item.value);
				}
			}
			this.setData({
				list
			})
			this.hidePicker();
			this.triggerEvent('chooseEvent', {
				chooseArray: this.data.list
			});
		}
	}
})

组件wxss

page {
    height: 100%;
    width: 100%;
}

.showPicker {
    width: 100%;
    height: 80rpx;
    line-height: 80rpx;
    font-size: 30rpx;
    background-color: paleturquoise;
    text-align: center;
}

.shade-container {
    position: fixed;
    height: 100%;
    width: 100%;
    top: 0;
    right: -200%;
    display: flex;
    justify-content: space-around;
    background-color: rgba(0, 0, 0, 0.5);
    transform: translateX(-200%);
    transition: all 0.5s ease;
    z-index: 9999;
}

.hide-container {
    position: fixed;
    height: 100%;
    width: 100%;
    top: 0;
    right: -200%;
    z-index: 9999;
    display: flex;
    justify-content: space-between;
    transform: translateX(100%);
    transition: all 0.5s ease-in;
}

.left-shade {
    width: 30%;
    height: 100%;
}

.right-choose {
    width: 70%;
    height: 100%;
    background-color: #fff;
    padding: 40rpx;
}

.picker-container {
    height: calc(100% - 200rpx);
    overflow-x: hidden;
    overflow-y: scroll;
    margin-top: 40rpx;
}

.picker-container::-webkit-scrollbar {
    display: none;
}

.picker-item {
    width: calc(100% - 8rpx);
    height: 50rpx;
    line-height: 50rpx;
    font-size: 24rpx;
    text-align: center;
    margin-top: 20rpx;
    border: 2rpx solid #eaeaea;
    border-radius: 8rpx;
}

.picker-item-choose {
    border: 2rpx solid rgb(110, 216, 84);
}

.picker-item:nth-of-type(1) {
    margin: 0;
}

.button-container {
    width: 100%;
    height: 80rpx;
    display: flex;
    justify-content: space-between;
    font-size: 24rpx;
    text-align: center;
    border-bottom: 2rpx solid #eaeaea;
}

.cancal {
    width: 100rpx;
    height: 40rpx;
    line-height: 40rpx;
    border: 2rpx solid #ddd;
    border-radius: 8rpx;
}

.sure {
    width: 100rpx;
    height: 40rpx;
    line-height: 40rpx;
    border: 2rpx solid rgb(132, 235, 132);
    border-radius: 8rpx;
}

引入页json

{
	"usingComponents": {
		"right-picker": "../../Component/right-picker/right-picker"
	}
}

引入页wxml

<right-picker
	bind:chooseEvent='choose'
	chooseList='{{ chooseList }}'
	multiple='{{ multiple }}'
></right-picker>

引入页js

Page({
	data: {
		chooseList: [
			{ value: 'Java', name: 'Java' },
			{ value: 'C', name: 'C' },
			{ value: 'Python', name: 'Python' },
			{ value: 'C++', name: 'C++' },
			{ value: 'JavaScript', name: 'JavaScript' },
			{ value: 'Ruby', name: 'Ruby' }
		],
		multiple: true
	},
	// 点击确定事件
	choose(e) {
		this.setData({
			chooseArray: e.detail.chooseArray
		})
		console.log(this.data.chooseArray);
	}
})