mult-select.vue 7.66 KB
<template>
	<view class="u-select">
		<u-popup :maskCloseAble="maskCloseAble" mode="bottom" :popup="false" v-model="value" length="auto"
			:safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex">
			<view class="u-select">
				<view class="u-select__header" @touchmove.stop.prevent="">
					<view class="u-select__header__cancel u-select__header__btn" :style="{ color: cancelColor }"
						hover-class="u-hover-class" :hover-stay-time="150" @tap="getResult('cancel')">
						{{cancelText}}
					</view>
					<view class="u-select__header__title">
						{{title}}
					</view>
					<view class="u-select__header__confirm u-select__header__btn"
						:style="{ color: moving ? cancelColor : confirmColor }" hover-class="u-hover-class"
						:hover-stay-time="150" @touchmove.stop="" @tap.stop="getResult('confirm')">
						{{confirmText}}
					</view>
				</view>
				<view class="u-select__body u-select__body__multiple">
					<scroll-view :scroll-y="true" style="height: 100%">
						<u-checkbox-group @change="checkboxGroupChange" wrap>
							<u-checkbox v-model="item.checked" :name="item[valueName]" v-for="(item,i) in columnData"
								:key="i">{{item[labelName]}}</u-checkbox>
						</u-checkbox-group>
					</scroll-view>
				</view>
			</view>
		</u-popup>
	</view>
</template>

<script>
	/**
	 * select 列选择器
	 * @description 此选择器用于单列,多列,多列联动的选择场景。(从1.3.0版本起,不建议使用Picker组件的单列和多列模式,Select组件是专门为列选择而构造的组件,更简单易用。)
	 * @tutorial http://uviewui.com/components/select.html
	 * @property {Array} list 列数据,数组形式,见官网说明
	 * @property {Boolean} v-model 布尔值变量,用于控制选择器的弹出与收起
	 * @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
	 * @property {String} cancel-color 取消按钮的颜色(默认#606266)
	 * @property {String} confirm-color 确认按钮的颜色(默认#2979ff)
	 * @property {String} confirm-text 确认按钮的文字
	 * @property {String} cancel-text 取消按钮的文字
	 * @property {String} default-value 提供的默认选中的下标,见官网说明
	 * @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker(默认true)
	 * @property {String Number} z-index 弹出时的z-index值(默认10075)
	 * @property {String} value-name 自定义list数据的value属性名 1.3.6
	 * @property {String} label-name 自定义list数据的label属性名 1.3.6
	 * @property {String} child-name 自定义list数据的children属性名,只对多列联动模式有效 1.3.7
	 * @event {Function} confirm 点击确定按钮,返回当前选择的值
	 * @example <u-select v-model="show" :list="list"></u-select>
	 */

	export default {
		props: {
			// 列数据
			list: {
				type: Array,
				default () {
					return [];
				}
			},
			// 是否显示边框
			border: {
				type: Boolean,
				default: true
			},
			// 通过双向绑定控制组件的弹出与收起
			value: {
				type: Boolean,
				default: false
			},
			// "取消"按钮的颜色
			cancelColor: {
				type: String,
				default: '#606266'
			},
			// "确定"按钮的颜色
			confirmColor: {
				type: String,
				default: '#2979ff'
			},
			// 弹出的z-index值
			zIndex: {
				type: [String, Number],
				default: 0
			},
			safeAreaInsetBottom: {
				type: Boolean,
				default: false
			},
			// 是否允许通过点击遮罩关闭Picker
			maskCloseAble: {
				type: Boolean,
				default: true
			},
			// 提供的默认选中的下标
			defaultValue: {
				type: Array,
				default () {
					return [];
				}
			},
			// 自定义value属性名
			valueName: {
				type: String,
				default: 'value'
			},
			// 自定义label属性名
			labelName: {
				type: String,
				default: 'label'
			},
			// 自定义多列联动模式的children属性名
			childName: {
				type: String,
				default: 'children'
			},
			// 顶部标题
			title: {
				type: String,
				default: ''
			},
			// 取消按钮的文字
			cancelText: {
				type: String,
				default: '取消'
			},
			// 确认按钮的文字
			confirmText: {
				type: String,
				default: '确认'
			}
		},
		data() {
			return {
				// 用于列改变时,保存当前的索引,下一次变化时比较得出是哪一列发生了变化
				defaultSelector: [],
				// picker-view的数据
				columnData: [],
				// 每次队列发生变化时,保存选择的结果
				selectValue: [],
				// 上一次列变化时的index
				lastSelectIndex: [],
				// 列是否还在滑动中,微信小程序如果在滑动中就点确定,结果可能不准确
				moving: false
			};
		},
		watch: {
			// 在select弹起的时候,重新初始化所有数据
			value: {
				immediate: true,
				handler(val) {
					if (val) setTimeout(() => this.init(), 10);
				}
			},
		},
		computed: {
			uZIndex() {
				// 如果用户有传递z-index值,优先使用
				return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
			},
		},
		methods: {
			// 标识滑动开始,只有微信小程序才有这样的事件
			pickstart() {
				// #ifdef MP-WEIXIN
				this.moving = true;
				// #endif
			},
			// 标识滑动结束
			pickend() {
				// #ifdef MP-WEIXIN
				this.moving = false;
				// #endif
			},
			init() {
				this.setColumnData();
				this.setSelectValue();
			},
			// 获取需要展示在picker中的列数据
			setColumnData() {
				this.columnData = this.list.map((o, i) => ({
					...o,
					checked: false
				}))
			},
			// 获取默认选中的值
			setSelectValue() {
				this.selectValue = this.defaultValue
				outer: for (let i = 0; i < this.defaultValue.length; i++) {
					inner: for (let j = 0; j < this.columnData.length; j++) {
						if (this.defaultValue[i] === this.columnData[j][this.valueName]) {
							this.columnData[j].checked = true
							break inner
						}
					}
				}
			},
			checkboxGroupChange(e) {
				this.selectValue = e
			},
			close() {
				this.$emit('input', false);
			},
			// 点击确定或者取消
			getResult(event = null) {
				// #ifdef MP-WEIXIN
				if (this.moving) return;
				// #endif
				if (event) {
					let label = ''
					if (this.selectValue.length) {
						for (let i = 0; i < this.columnData.length; i++) {
							if (this.columnData[i].checked) {
								if (!label) {
									label += this.columnData[i][this.labelName]
								} else {
									label += ',' + this.columnData[i][this.labelName]
								}
							}
						}
					}
					this.$emit(event, this.selectValue, label);
				}
				this.close();
			},
			selectHandler() {
				this.$emit('click');
			}
		}
	};
</script>

<style scoped lang="scss">
	@import "@/uview-ui/libs/css/style.components.scss";

	.u-select {

		&__action {
			position: relative;
			line-height: $u-form-item-height;
			height: $u-form-item-height;

			&__icon {
				position: absolute;
				right: 20rpx;
				top: 50%;
				transition: transform .4s;
				transform: translateY(-50%);
				z-index: 1;

				&--reverse {
					transform: rotate(-180deg) translateY(50%);
				}
			}
		}

		&__hader {
			&__title {
				color: $u-content-color;
			}
		}

		&--border {
			border-radius: 6rpx;
			border-radius: 4px;
			border: 1px solid $u-form-item-border-color;
		}

		&__header {
			@include vue-flex;
			align-items: center;
			justify-content: space-between;
			height: 80rpx;
			padding: 0 40rpx;
		}

		&__body {
			width: 100%;
			height: 500rpx;
			overflow: hidden;
			background-color: #fff;

			&__picker-view {
				height: 100%;
				box-sizing: border-box;

				&__item {
					@include vue-flex;
					align-items: center;
					justify-content: center;
					font-size: 32rpx;
					color: $u-main-color;
					padding: 0 8rpx;
				}
			}
		}
	}
</style>