mirror of
				https://gitee.com/vant-contrib/vant-weapp.git
				synced 2025-10-31 03:32:07 +08:00 
			
		
		
		
	[new feature] Picker: add new component Picker
This commit is contained in:
		
							parent
							
								
									c1faa8b130
								
							
						
					
					
						commit
						3cb142b8f5
					
				| @ -37,7 +37,8 @@ | |||||||
|     "pages/swipe-cell/index", |     "pages/swipe-cell/index", | ||||||
|     "pages/datetime-picker/index", |     "pages/datetime-picker/index", | ||||||
|     "pages/rate/index", |     "pages/rate/index", | ||||||
|     "pages/collapse/index" |     "pages/collapse/index", | ||||||
|  |     "pages/picker/index" | ||||||
|   ], |   ], | ||||||
|   "window": { |   "window": { | ||||||
|     "navigationBarBackgroundColor": "#f8f8f8", |     "navigationBarBackgroundColor": "#f8f8f8", | ||||||
| @ -94,6 +95,7 @@ | |||||||
|     "van-datetime-picker": "../../dist/datetime-picker/index", |     "van-datetime-picker": "../../dist/datetime-picker/index", | ||||||
|     "van-rate": "../../dist/rate/index", |     "van-rate": "../../dist/rate/index", | ||||||
|     "van-collapse": "../../dist/collapse/index", |     "van-collapse": "../../dist/collapse/index", | ||||||
|     "van-collapse-item": "../../dist/collapse-item/index" |     "van-collapse-item": "../../dist/collapse-item/index", | ||||||
|  |     "van-picker": "../../dist/picker/index" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -43,6 +43,10 @@ export default [ | |||||||
|         path: '/field', |         path: '/field', | ||||||
|         title: 'Field 输入框' |         title: 'Field 输入框' | ||||||
|       }, |       }, | ||||||
|  |       { | ||||||
|  |         path: '/picker', | ||||||
|  |         title: 'Picker 选择器' | ||||||
|  |       }, | ||||||
|       { |       { | ||||||
|         path: '/radio', |         path: '/radio', | ||||||
|         title: 'Radio 单选框' |         title: 'Radio 单选框' | ||||||
|  | |||||||
							
								
								
									
										48
									
								
								example/pages/picker/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								example/pages/picker/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | import Page from '../../common/page'; | ||||||
|  | import Toast from '../../dist/toast/toast'; | ||||||
|  | 
 | ||||||
|  | Page({ | ||||||
|  |   data: { | ||||||
|  |     column1: ['杭州', '宁波', '温州', '嘉兴', '湖州'], | ||||||
|  |     column2: [ | ||||||
|  |       { text: '杭州', disabled: true }, | ||||||
|  |       { text: '宁波' }, | ||||||
|  |       { text: '温州' } | ||||||
|  |     ], | ||||||
|  |     column3: { | ||||||
|  |       浙江: ['杭州', { text: '宁波' }, { text: '温州', disabled: true }, '嘉兴', '湖州'], | ||||||
|  |       福建: ['福州', '厦门', '莆田', '三明', '泉州'] | ||||||
|  |     }, | ||||||
|  |     column4: [ | ||||||
|  |       { | ||||||
|  |         values: ['浙江', '福建'], | ||||||
|  |         className: 'column1' | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         values: ['杭州', '宁波', '温州', '嘉兴', '湖州'], | ||||||
|  |         className: 'column2', | ||||||
|  |         defaultIndex: 2 | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   onChange1(event) { | ||||||
|  |     const { value, index } = event.detail; | ||||||
|  |     Toast(`Value: ${value}, Index:${index}`); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   onConfirm(event) { | ||||||
|  |     const { value, index } = event.detail; | ||||||
|  |     Toast(`Value: ${value}, Index:${index}`); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   onCancel() { | ||||||
|  |     Toast('取消'); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   onChange2(event) { | ||||||
|  |     const { picker, value } = event.detail; | ||||||
|  |     picker.setColumnValues(1, this.data.column3[value[0]]); | ||||||
|  |     getApp().picker = picker; | ||||||
|  |   } | ||||||
|  | }); | ||||||
							
								
								
									
										3
									
								
								example/pages/picker/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								example/pages/picker/index.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | { | ||||||
|  |   "navigationBarTitleText": "Picker 选择器" | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								example/pages/picker/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								example/pages/picker/index.wxml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | <demo-block title="基础用法"> | ||||||
|  |   <van-picker | ||||||
|  |     columns="{{ column1 }}" | ||||||
|  |     bind:change="onChange1" | ||||||
|  |   /> | ||||||
|  | </demo-block> | ||||||
|  | 
 | ||||||
|  | <demo-block title="禁用选项"> | ||||||
|  |   <van-picker | ||||||
|  |     columns="{{ column2 }}" | ||||||
|  |   /> | ||||||
|  | </demo-block> | ||||||
|  | 
 | ||||||
|  | <demo-block title="展示顶部栏"> | ||||||
|  |   <van-picker | ||||||
|  |     show-toolbar | ||||||
|  |     title="标题" | ||||||
|  |     columns="{{ column1 }}" | ||||||
|  |     bind:change="onChange1" | ||||||
|  |     bind:confirm="onConfirm" | ||||||
|  |     bind:cancel="onCancel" | ||||||
|  |   /> | ||||||
|  | </demo-block> | ||||||
|  | 
 | ||||||
|  | <demo-block title="多列联动"> | ||||||
|  |   <van-picker | ||||||
|  |     columns="{{ column4 }}" | ||||||
|  |     bind:change="onChange2" | ||||||
|  |   /> | ||||||
|  | </demo-block> | ||||||
|  | 
 | ||||||
|  | <demo-block title="加载状态"> | ||||||
|  |   <van-picker | ||||||
|  |     loading | ||||||
|  |     columns="{{ column4 }}" | ||||||
|  |   /> | ||||||
|  | </demo-block> | ||||||
|  | 
 | ||||||
|  | <van-toast id="van-toast" /> | ||||||
							
								
								
									
										0
									
								
								example/pages/picker/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								example/pages/picker/index.wxss
									
									
									
									
									
										Normal file
									
								
							| @ -11,8 +11,13 @@ function isNumber(value) { | |||||||
|   return /^\d+$/.test(value); |   return /^\d+$/.test(value); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | function range(num: number, min: number, max: number) { | ||||||
|  |   return Math.min(Math.max(num, min), max); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export { | export { | ||||||
|   isObj, |   isObj, | ||||||
|   isDef, |   isDef, | ||||||
|   isNumber |   isNumber, | ||||||
|  |   range | ||||||
| }; | }; | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								packages/picker-column/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/picker-column/index.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | { | ||||||
|  |   "component": true | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								packages/picker-column/index.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								packages/picker-column/index.less
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | @import '../common/style/var'; | ||||||
|  | 
 | ||||||
|  | .van-picker-column { | ||||||
|  |   overflow: hidden; | ||||||
|  |   font-size: 16px; | ||||||
|  |   text-align: center; | ||||||
|  | 
 | ||||||
|  |   &__item { | ||||||
|  |     padding: 0 5px; | ||||||
|  |     color: @gray-dark; | ||||||
|  | 
 | ||||||
|  |     &--selected { | ||||||
|  |       font-weight: 500; | ||||||
|  |       color: @text-color; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &--disabled { | ||||||
|  |       opacity: 0.3; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										160
									
								
								packages/picker-column/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								packages/picker-column/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,160 @@ | |||||||
|  | import { VantComponent } from '../common/component'; | ||||||
|  | import { isObj, range } from '../common/utils'; | ||||||
|  | 
 | ||||||
|  | const DEFAULT_DURATION = 200; | ||||||
|  | 
 | ||||||
|  | VantComponent({ | ||||||
|  |   classes: ['active-class'], | ||||||
|  | 
 | ||||||
|  |   props: { | ||||||
|  |     valueKey: String, | ||||||
|  |     className: String, | ||||||
|  |     itemHeight: Number, | ||||||
|  |     visibleItemCount: Number, | ||||||
|  |     initialOptions: { | ||||||
|  |       type: Array, | ||||||
|  |       value: [] | ||||||
|  |     }, | ||||||
|  |     defaultIndex: { | ||||||
|  |       type: Number, | ||||||
|  |       value: 0 | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   data: { | ||||||
|  |     startY: 0, | ||||||
|  |     offset: 0, | ||||||
|  |     duration: 0, | ||||||
|  |     startOffset: 0, | ||||||
|  |     options: [], | ||||||
|  |     currentIndex: 0 | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   created() { | ||||||
|  |     const { defaultIndex, initialOptions } = this.data; | ||||||
|  |     this.set({ | ||||||
|  |       currentIndex: defaultIndex, | ||||||
|  |       options: initialOptions | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   computed: { | ||||||
|  |     count() { | ||||||
|  |       return this.data.options.length; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     baseOffset() { | ||||||
|  |       const { data } = this; | ||||||
|  |       return (data.itemHeight * (data.visibleItemCount - 1)) / 2; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     wrapperStyle() { | ||||||
|  |       const { data } = this; | ||||||
|  |       return [ | ||||||
|  |         `transition: ${data.duration}ms`, | ||||||
|  |         `transform: translate3d(0, ${data.offset + data.baseOffset}px, 0)`, | ||||||
|  |         `line-height: ${data.itemHeight}px` | ||||||
|  |       ].join('; '); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   watch: { | ||||||
|  |     defaultIndex(value: number) { | ||||||
|  |       this.setIndex(value); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   methods: { | ||||||
|  |     onTouchStart(event: Weapp.TouchEvent) { | ||||||
|  |       this.set({ | ||||||
|  |         startY: event.touches[0].clientY, | ||||||
|  |         startOffset: this.data.offset, | ||||||
|  |         duration: 0 | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     onTouchMove(event: Weapp.TouchEvent) { | ||||||
|  |       const { data } = this; | ||||||
|  |       const deltaY = event.touches[0].clientY - data.startY; | ||||||
|  |       this.set({ | ||||||
|  |         offset: range( | ||||||
|  |           data.startOffset + deltaY, | ||||||
|  |           -(data.count * data.itemHeight), | ||||||
|  |           data.itemHeight | ||||||
|  |         ) | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     onTouchEnd() { | ||||||
|  |       const { data } = this; | ||||||
|  |       if (data.offset !== data.startOffset) { | ||||||
|  |         this.set({ | ||||||
|  |           duration: DEFAULT_DURATION | ||||||
|  |         }); | ||||||
|  |         const index = range( | ||||||
|  |           Math.round(-data.offset / data.itemHeight), | ||||||
|  |           0, | ||||||
|  |           data.count - 1 | ||||||
|  |         ); | ||||||
|  |         this.setIndex(index, true); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     onClickItem(event: Weapp.Event) { | ||||||
|  |       const { index } = event.currentTarget.dataset; | ||||||
|  |       this.setIndex(index, true); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     adjustIndex(index: number) { | ||||||
|  |       const { data } = this; | ||||||
|  |       index = range(index, 0, data.count); | ||||||
|  |       for (let i = index; i < data.count; i++) { | ||||||
|  |         if (!this.isDisabled(data.options[i])) return i; | ||||||
|  |       } | ||||||
|  |       for (let i = index - 1; i >= 0; i--) { | ||||||
|  |         if (!this.isDisabled(data.options[i])) return i; | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     isDisabled(option: any) { | ||||||
|  |       return isObj(option) && option.disabled; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     getOptionText(option: any) { | ||||||
|  |       const { data } = this; | ||||||
|  |       return isObj(option) && data.valueKey in option | ||||||
|  |         ? option[data.valueKey] | ||||||
|  |         : option; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     setIndex(index: number, userAction: boolean) { | ||||||
|  |       const { data } = this; | ||||||
|  |       index = this.adjustIndex(index) || 0; | ||||||
|  | 
 | ||||||
|  |       this.set({ | ||||||
|  |         offset: -index * data.itemHeight | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       if (index !== data.currentIndex) { | ||||||
|  |         this.set({ | ||||||
|  |           currentIndex: index | ||||||
|  |         }); | ||||||
|  |         userAction && this.$emit('change', index); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     setValue(value: string) { | ||||||
|  |       const { options } = this.data; | ||||||
|  |       for (let i = 0; i < options.length; i++) { | ||||||
|  |         if (this.getOptionText(options[i]) === value) { | ||||||
|  |           return this.setIndex(i); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     getValue() { | ||||||
|  |       const { data } = this; | ||||||
|  |       return data.options[data.currentIndex]; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }); | ||||||
							
								
								
									
										31
									
								
								packages/picker-column/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								packages/picker-column/index.wxml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | |||||||
|  | <view | ||||||
|  |   class="van-picker-column custom-class" | ||||||
|  |   style="height: {{ itemHeight * visibleItemCount }}px" | ||||||
|  |   bind:touchstart="onTouchStart" | ||||||
|  |   catch:touchmove="onTouchMove" | ||||||
|  |   bind:touchend="onTouchEnd" | ||||||
|  |   bind:touchcancel="onTouchEnd" | ||||||
|  | > | ||||||
|  |   <view style="{{ wrapperStyle }}"> | ||||||
|  |     <view | ||||||
|  |       wx:for="{{ options }}" | ||||||
|  |       wx:for-item="option" | ||||||
|  |       wx:key="index" | ||||||
|  |       data-index="{{ index }}" | ||||||
|  |       style="height: {{ itemHeight }}px" | ||||||
|  |       class="van-ellipsis van-picker-column__item {{ option && option.disabled ? 'van-picker-column__item--disabled' : '' }} {{ index === currentIndex ? 'van-picker-column__item--selected active-class' : '' }}" | ||||||
|  |       bindtap="onClickItem" | ||||||
|  |     >{{ getOptionText(option, valueKey) }}</view> | ||||||
|  |   </view> | ||||||
|  | </view> | ||||||
|  | 
 | ||||||
|  | <wxs module="getOptionText"> | ||||||
|  | function isObj(x) { | ||||||
|  |   var type = typeof x; | ||||||
|  |   return x !== null && (type === 'object' || type === 'function'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = function (option, valueKey) { | ||||||
|  |   return isObj(option) && option[valueKey] ? option[valueKey] : option; | ||||||
|  | } | ||||||
|  | </wxs> | ||||||
							
								
								
									
										185
									
								
								packages/picker/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								packages/picker/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | |||||||
|  | ## Picker 选择器 | ||||||
|  | 选择器组件通常与 [弹出层](#/popup) 组件配合使用 | ||||||
|  | 
 | ||||||
|  | ### 使用指南 | ||||||
|  | 在 app.json 或 index.json 中引入组件 | ||||||
|  | ```json | ||||||
|  | "usingComponents": { | ||||||
|  |   "van-picker": "path/to/vant-weapp/dist/picker/index" | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### 代码演示 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #### 基础用法 | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <van-picker columns="{{ columns }}" bind:change="onChange" /> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | import Toast from 'path/to/vant-weapp/dist/toast/toast'; | ||||||
|  | 
 | ||||||
|  | Page({ | ||||||
|  |   data: { | ||||||
|  |     columns: ['杭州', '宁波', '温州', '嘉兴', '湖州'] | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   onChange(event) { | ||||||
|  |     const { picker, value, index } = event.detail; | ||||||
|  |     Toast(`当前值:${value}, 当前索引:${index}`); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | #### 禁用选项 | ||||||
|  | 选项可以为对象结构,通过设置 disabled 来禁用该选项 | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <van-picker columns="{{ columns }}" /> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | Page({ | ||||||
|  |   data: { | ||||||
|  |     columns: [ | ||||||
|  |       { text: '杭州', disabled: true }, | ||||||
|  |       { text: '宁波' }, | ||||||
|  |       { text: '温州' } | ||||||
|  |     ] | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | #### 展示顶部栏 | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <van-picker | ||||||
|  |   show-toolbar | ||||||
|  |   title="标题" | ||||||
|  |   columns="{{ columns }}" | ||||||
|  |   bind:cancel="onCancel" | ||||||
|  |   bind:confirm="onConfirm" | ||||||
|  | /> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | import Toast from 'path/to/vant-weapp/dist/toast/toast'; | ||||||
|  | 
 | ||||||
|  | Page({ | ||||||
|  |   data: { | ||||||
|  |     columns: ['杭州', '宁波', '温州', '嘉兴', '湖州'] | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   onConfirm(event) { | ||||||
|  |     const { picker, value, index } = event.detail; | ||||||
|  |     Toast(`当前值:${value}, 当前索引:${index}`); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   onCancel() { | ||||||
|  |     Toast('取消'); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | #### 多列联动 | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <van-picker columns="{{ columns }}" bind:change="onChange" /> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ```javascript | ||||||
|  | const citys = { | ||||||
|  |   '浙江': ['杭州', '宁波', '温州', '嘉兴', '湖州'], | ||||||
|  |   '福建': ['福州', '厦门', '莆田', '三明', '泉州'] | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Page({ | ||||||
|  |   data: { | ||||||
|  |     columns: [ | ||||||
|  |       { | ||||||
|  |         values: Object.keys(citys), | ||||||
|  |         className: 'column1' | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         values: citys['浙江'], | ||||||
|  |         className: 'column2', | ||||||
|  |         defaultIndex: 2 | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   onChange(event) { | ||||||
|  |     const { picker, value, index } = event.detail; | ||||||
|  |     picker.setColumnValues(1, citys[values[0]]); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | #### 加载状态 | ||||||
|  | 当 Picker 数据是通过异步获取时,可以通过 `loading` 属性显示加载提示 | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <van-picker columns="{{ columns }}" loading /> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### API | ||||||
|  | 
 | ||||||
|  | | 参数 | 说明 | 类型 | 默认值 | 版本 | | ||||||
|  | |------|------|------|------|------| | ||||||
|  | | columns | 对象数组,配置每一列显示的数据 | `Array` | `[]` | - | | ||||||
|  | | show-toolbar | 是否显示顶部栏 | `Boolean` | `false` | - | | ||||||
|  | | title | 顶部栏标题 | `String` | `''` | - | | ||||||
|  | | loading | 是否显示加载状态 | `Boolean` | `false` | - | | ||||||
|  | | value-key | 选项对象中,文字对应的 key | `String` | `text` | - | | ||||||
|  | | item-height | 选项高度 | `Number` | `44` | - | | ||||||
|  | | confirm-button-text | 确认按钮文字 | `String` | `确认` | - | | ||||||
|  | | cancel-button-text | 取消按钮文字 | `String` | `取消` | - | | ||||||
|  | | visible-item-count | 可见的选项个数 | `Number` | `5` | - | | ||||||
|  | 
 | ||||||
|  | ### Event | ||||||
|  | 
 | ||||||
|  | Picker 组件的事件会根据 columns 是单列或多列返回不同的参数 | ||||||
|  | 
 | ||||||
|  | | 事件名 | 说明 | 参数 | | ||||||
|  | |------|------|------| | ||||||
|  | | confirm | 点击完成按钮时触发 | 单列:选中值,选中值对应的索引<br>多列:所有列选中值,所有列选中值对应的索引 | | ||||||
|  | | cancel | 点击取消按钮时触发 | 单列:选中值,选中值对应的索引<br>多列:所有列选中值,所有列选中值对应的索引 | | ||||||
|  | | change | 选项改变时触发 | 单列:Picker 实例,选中值,选中值对应的索引<br>多列:Picker 实例,所有列选中值,当前列对应的索引 | | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Columns 数据结构 | ||||||
|  | 
 | ||||||
|  | 当传入多列数据时,`columns`为一个对象数组,数组中的每一个对象配置每一列,每一列有以下`key` | ||||||
|  | 
 | ||||||
|  | | key | 说明 | | ||||||
|  | |------|------| | ||||||
|  | | values | 列中对应的备选值 | | ||||||
|  | | defaultIndex | 初始选中项的索引,默认为 0 | | ||||||
|  | 
 | ||||||
|  | ### 外部样式类 | ||||||
|  | 
 | ||||||
|  | | 类名 | 说明 | | ||||||
|  | |-----------|-----------| | ||||||
|  | | custom-class | 根节点样式类 | | ||||||
|  | | active-class | 选中项样式类 | | ||||||
|  | | toolbar-class | 顶部栏样式类 | | ||||||
|  | | column-class | 列样式类 | | ||||||
|  | 
 | ||||||
|  | ### 方法 | ||||||
|  | 
 | ||||||
|  | 通过 selectComponent 可以获取到 picker 实例并调用实例方法 | ||||||
|  | 
 | ||||||
|  | | 方法名 | 参数 | 返回值 | 介绍 | | ||||||
|  | |------|------|------|------| | ||||||
|  | | getValues | - | values | 获取所有列选中的值 | | ||||||
|  | | setValues | values | - | 设置所有列选中的值 | | ||||||
|  | | getIndexes | - | indexes | 获取所有列选中值对应的索引 | | ||||||
|  | | setIndexes | indexes | - | 设置所有列选中值对应的索引 | | ||||||
|  | | getColumnValue | columnIndex | value | 获取对应列选中的值 | | ||||||
|  | | setColumnValue | columnIndex, value | - | 设置对应列选中的值 | | ||||||
|  | | getColumnIndex | columnIndex | optionIndex | 获取对应列选中项的索引 | | ||||||
|  | | setColumnIndex | columnIndex, optionIndex | - | 设置对应列选中项的索引 | | ||||||
|  | | getColumnValues | columnIndex | values | 获取对应列中所有选项 | | ||||||
|  | | setColumnValues | columnIndex, values | - | 设置对应列中所有选项 | | ||||||
							
								
								
									
										7
									
								
								packages/picker/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/picker/index.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | { | ||||||
|  |   "component": true, | ||||||
|  |   "usingComponents": { | ||||||
|  |     "picker-column": "../picker-column/index", | ||||||
|  |     "loading": "../loading/index" | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										67
									
								
								packages/picker/index.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								packages/picker/index.less
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | |||||||
|  | @import '../common/style/var'; | ||||||
|  | 
 | ||||||
|  | .van-picker { | ||||||
|  |   position: relative; | ||||||
|  |   overflow: hidden; | ||||||
|  |   -webkit-text-size-adjust: 100%; /* avoid iOS text size adjust */ | ||||||
|  |   background-color: @white; | ||||||
|  |   user-select: none; | ||||||
|  | 
 | ||||||
|  |   &__toolbar { | ||||||
|  |     display: flex; | ||||||
|  |     height: 44px; | ||||||
|  |     line-height: 44px; | ||||||
|  |     justify-content: space-between; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__cancel, | ||||||
|  |   &__confirm { | ||||||
|  |     padding: 0 15px; | ||||||
|  |     font-size: 14px; | ||||||
|  |     color: @blue; | ||||||
|  | 
 | ||||||
|  |     &:active { | ||||||
|  |       background-color: @active-color; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__title { | ||||||
|  |     max-width: 50%; | ||||||
|  |     font-size: 16px; | ||||||
|  |     font-weight: 500; | ||||||
|  |     text-align: center; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__columns { | ||||||
|  |     position: relative; | ||||||
|  |     display: flex; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__column { | ||||||
|  |     flex: 1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__loading { | ||||||
|  |     position: absolute; | ||||||
|  |     top: 0; | ||||||
|  |     right: 0; | ||||||
|  |     bottom: 0; | ||||||
|  |     left: 0; | ||||||
|  |     z-index: 4; | ||||||
|  |     display: flex; | ||||||
|  |     background-color: rgba(255, 255, 255, 0.9); | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: center; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__loading .van-loading, | ||||||
|  |   &__frame { | ||||||
|  |     position: absolute; | ||||||
|  |     top: 50%; | ||||||
|  |     left: 0; | ||||||
|  |     z-index: 1; | ||||||
|  |     width: 100%; | ||||||
|  |     pointer-events: none; | ||||||
|  |     transform: translateY(-50%); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										154
									
								
								packages/picker/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								packages/picker/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,154 @@ | |||||||
|  | import { VantComponent } from '../common/component'; | ||||||
|  | 
 | ||||||
|  | VantComponent({ | ||||||
|  |   classes: ['active-class', 'toolbar-class', 'column-class'], | ||||||
|  | 
 | ||||||
|  |   props: { | ||||||
|  |     title: String, | ||||||
|  |     loading: Boolean, | ||||||
|  |     showToolbar: Boolean, | ||||||
|  |     confirmButtonText: String, | ||||||
|  |     cancelButtonText: String, | ||||||
|  |     visibleItemCount: { | ||||||
|  |       type: Number, | ||||||
|  |       value: 5 | ||||||
|  |     }, | ||||||
|  |     valueKey: { | ||||||
|  |       type: String, | ||||||
|  |       value: 'text' | ||||||
|  |     }, | ||||||
|  |     itemHeight: { | ||||||
|  |       type: Number, | ||||||
|  |       value: 44 | ||||||
|  |     }, | ||||||
|  |     columns: { | ||||||
|  |       type: Array, | ||||||
|  |       value: [], | ||||||
|  |       observer(columns = []) { | ||||||
|  |         this.set({ | ||||||
|  |           simple: columns.length && !columns[0].values | ||||||
|  |         }, () => { | ||||||
|  |           const children = this.children = this.selectAllComponents('.van-picker__column'); | ||||||
|  | 
 | ||||||
|  |           if (Array.isArray(children) && children.length) { | ||||||
|  |             this.setColumns(); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   methods: { | ||||||
|  |     noop() {}, | ||||||
|  | 
 | ||||||
|  |     setColumns() { | ||||||
|  |       const { data } = this; | ||||||
|  |       const columns = data.simple ? [{ values: data.columns }] : data.columns; | ||||||
|  |       columns.forEach((columns, index: number) => { | ||||||
|  |         this.setColumnValues(index, columns.values); | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     emit(event: Weapp.Event) { | ||||||
|  |       const { type } = event.currentTarget.dataset; | ||||||
|  |       if (this.data.simple) { | ||||||
|  |         this.$emit(type, { | ||||||
|  |           value: this.getColumnValue(0), | ||||||
|  |           index: this.getColumnIndex(0) | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         this.$emit(type, { | ||||||
|  |           value: this.getValues(), | ||||||
|  |           index: this.getIndexes() | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     onChange(event: Weapp.Event) { | ||||||
|  |       if (this.data.simple) { | ||||||
|  |         this.$emit('change', { | ||||||
|  |           picker: this, | ||||||
|  |           value: this.getColumnValue(0), | ||||||
|  |           index: this.getColumnIndex(0) | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         this.$emit('change', { | ||||||
|  |           picker: this, | ||||||
|  |           value: this.getValues(), | ||||||
|  |           index: event.currentTarget.dataset.index | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // get column instance by index
 | ||||||
|  |     getColumn(index: number) { | ||||||
|  |       return this.children[index]; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // get column value by index
 | ||||||
|  |     getColumnValue(index: number) { | ||||||
|  |       const column = this.getColumn(index); | ||||||
|  |       return column && column.getValue(); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // set column value by index
 | ||||||
|  |     setColumnValue(index: number, value: any) { | ||||||
|  |       const column = this.getColumn(index); | ||||||
|  |       column && column.setValue(value); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // get column option index by column index
 | ||||||
|  |     getColumnIndex(columnIndex: number) { | ||||||
|  |       return (this.getColumn(columnIndex) || {}).data.currentIndex; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // set column option index by column index
 | ||||||
|  |     setColumnIndex(columnIndex: number, optionIndex: number) { | ||||||
|  |       const column = this.getColumn(columnIndex); | ||||||
|  |       column && column.setIndex(optionIndex); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // get options of column by index
 | ||||||
|  |     getColumnValues(index: number) { | ||||||
|  |       return (this.children[index] || {}).data.options; | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // set options of column by index
 | ||||||
|  |     setColumnValues(index: number, options: any[]) { | ||||||
|  |       const column = this.children[index]; | ||||||
|  | 
 | ||||||
|  |       if ( | ||||||
|  |         column && | ||||||
|  |         JSON.stringify(column.data.options) !== JSON.stringify(options) | ||||||
|  |       ) { | ||||||
|  |         column.set({ options }, () => { | ||||||
|  |           column.setIndex(0); | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // get values of all columns
 | ||||||
|  |     getValues() { | ||||||
|  |       return this.children.map((child: Weapp.Component) => child.getValue()); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // set values of all columns
 | ||||||
|  |     setValues(values: []) { | ||||||
|  |       values.forEach((value, index) => { | ||||||
|  |         this.setColumnValue(index, value); | ||||||
|  |       }); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // get indexes of all columns
 | ||||||
|  |     getIndexes() { | ||||||
|  |       return this.children.map((child: Weapp.Component) => child.data.currentIndex); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     // set indexes of all columns
 | ||||||
|  |     setIndexes(indexes: number[]) { | ||||||
|  |       indexes.forEach((optionIndex, columnIndex) => { | ||||||
|  |         this.setColumnIndex(columnIndex, optionIndex); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }); | ||||||
							
								
								
									
										41
									
								
								packages/picker/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								packages/picker/index.wxml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | <view class="van-picker custom-class"> | ||||||
|  |   <view | ||||||
|  |     wx:if="{{ showToolbar }}" | ||||||
|  |     class="van-picker__toolbar van-hairline--top-bottom toolbar-class" | ||||||
|  |   > | ||||||
|  |     <view class="van-picker__cancel" data-type="cancel" bindtap="emit"> | ||||||
|  |       {{ cancelButtonText || '取消' }} | ||||||
|  |     </view> | ||||||
|  |     <view wx:if="{{ title }}" class="van-picker__title van-ellipsis">{{ title }}</view> | ||||||
|  |     <view class="van-picker__confirm" data-type="confirm" bindtap="emit"> | ||||||
|  |       {{ confirmButtonText || '确认' }} | ||||||
|  |     </view> | ||||||
|  |   </view> | ||||||
|  |   <view wx:if="{{ loading }}" class="van-picker__loading"> | ||||||
|  |     <loading color="#1989fa"/> | ||||||
|  |   </view> | ||||||
|  |   <view | ||||||
|  |     class="van-picker__columns" | ||||||
|  |     style="height: {{ itemHeight * visibleItemCount }}px" | ||||||
|  |     catch:touchmove="noop" | ||||||
|  |   > | ||||||
|  |     <picker-column | ||||||
|  |       class="van-picker__column" | ||||||
|  |       wx:for="{{ simple ? [columns] : columns }}" | ||||||
|  |       wx:key="index" | ||||||
|  |       data-index="{{ index }}" | ||||||
|  |       custom-class="column-class" | ||||||
|  |       value-key="{{ valueKey }}" | ||||||
|  |       initial-options="{{ simple ? item : item.values }}" | ||||||
|  |       default-index="{{ item.defaultIndex }}" | ||||||
|  |       item-height="{{ itemHeight }}" | ||||||
|  |       visible-item-count="{{ visibleItemCount }}" | ||||||
|  |       active-class="active-class" | ||||||
|  |       bind:change="onChange" | ||||||
|  |     /> | ||||||
|  |     <view | ||||||
|  |       class="van-picker__frame van-hairline--top-bottom" | ||||||
|  |       style="height: {{ itemHeight }}px" | ||||||
|  |     /> | ||||||
|  |   </view> | ||||||
|  | </view> | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user