效果
需求
在项目实际开发中,微信小程序的picker组件满足不了业务需求并且存在一些弊端
例如:
- 实际开发时,需要在picker选择列表中的每一项加图标或者图片(用于展示不同状态);
- 微信小程序的picker组件在ios和android上的展示效果不同;
- 微信小程序的picker组件在android上的列表有无限循环的问题,而且反馈之后迟迟没有修复;
- 微信小程序的picker组件没有良好的多选模式(每次选择完后需要在点开选择,且在列表中没有明确的以选择标识);
说明
- 分装组件已完成,传参
multiple=true
为多选,multiple=false
为单选 - 用
<cover-view>
标签的原因是项目中可能会有echarts
,textarea
等等元素的层级影响,为了一劳永逸; - js文件中的
chooseList
是选择列表,可自行动态传入,已经做了列表超出后的滑动显示; - 选择完成后的
bind:chooseEvent
用于确定事件绑定,输出的是chooseList
的value
值,根据需求使用; - 实现原理其实不是很复杂,只不过从无到有也花了半天时间,不喜勿喷,转载请注明出处;
- 如有好的建议(例如滑动进入、退出的实现方式)或者技术讨论欢迎和本人联系;
代码
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);
}
})