【多选picker】uniapp改造uview组件,改造picker为多选,适用uniapp和vue

文章介绍了对uview的picker组件和checkbox组件进行改造的过程,包括去除错误提示、添加自定义checkbox组件以及在picker组件中使用自定义的checkbox来显示最后一级的选择项。改造后的组件可以适应多选需求,并提供了事件监听和数据交互功能。
摘要由CSDN通过智能技术生成

效果如图

在这里插入图片描述

复制一分uview的picker组件和checkbox组件的代码

  1. origin-component/custom-checkbox/custom-checkbox.vue
  • 注释掉了uni.$u.error(‘u-checkbox必须搭配u-checkbox-group组件使用’),避免为写在u-checkbox-group组件下控制台报错
<template>
	<view
	    class="u-checkbox"
	    :style="[checkboxStyle]"
	    @tap.stop="wrapperClickHandler"
	    :class="[`u-checkbox-label--${parentData.iconPlacement}`, parentData.borderBottom && parentData.placement === 'column' && 'u-border-bottom']"
	>
		<view
		    class="u-checkbox__icon-wrap"
		    @tap.stop="iconClickHandler"
		    :class="iconClasses"
		    :style="[iconWrapStyle]"
		>
			<slot name="icon">
				<u-icon
				    class="u-checkbox__icon-wrap__icon"
				    name="checkbox-mark"
				    :size="elIconSize"
				    :color="elIconColor"
				/>
			</slot>
		</view>
		<text
		    @tap.stop="labelClickHandler"
		    :style="{
				color: elDisabled ? elInactiveColor : elLabelColor,
				fontSize: elLabelSize,
				lineHeight: elLabelSize
			}"
		>{{label}}</text>
	</view>
</template>

<script>
	import props from './props.js';
	/**
	 * checkbox  复选框
	 * @description 复选框组件一般用于需要多个选择的场景,该组件功能完整,使用方便
	 * @tutorial https://uviewui.com/components/checkbox.html
	 * @property {String | Number | Boolean}	name			checkbox组件的标示符
	 * @property {String}						shape			形状,square为方形,circle为圆型
	 * @property {String | Number}				size			整体的大小
	 * @property {Boolean}						checked			是否默认选中
	 * @property {String | Boolean}				disabled		是否禁用
	 * @property {String}						activeColor		选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
	 * @property {String}						inactiveColor	未选中的颜色
	 * @property {String | Number}				iconSize		图标的大小,单位px
	 * @property {String}						iconColor		图标颜色
	 * @property {String | Number}				label			label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式
	 * @property {String}						labelColor 		label的颜色
	 * @property {String | Number}				labelSize		label的字体大小,px单位
	 * @property {String | Boolean}				labelDisabled	是否禁止点击提示语选中复选框
	 * @property {Object}						customStyle		定义需要用到的外部样式
	 * 
	 * @event {Function}	change	任一个checkbox状态发生变化时触发,回调为一个对象
	 * @example <u-checkbox v-model="checked" :disabled="false">天涯</u-checkbox>
	 */
	export default {
		name: "custom-checkbox",
		mixins: [uni.$u.mpMixin, uni.$u.mixin,props],
		data() {
			return {
				isChecked: false,
				// 父组件的默认值,因为头条小程序不支持在computed中使用this.parent.shape的形式
				// 故只能使用如此方法
				parentData: {
					iconSize: 12,
					labelDisabled: null,
					disabled: null,
					shape: 'square',
					activeColor: null,
					inactiveColor: null,
					size: 18,
					value: null,
					iconColor: null,
					placement: 'row',
					borderBottom: false,
					iconPlacement: 'left'
				}
			}
		},
		computed: {
			// 是否禁用,如果父组件u-raios-group禁用的话,将会忽略子组件的配置
			elDisabled() {
				return this.disabled !== '' ? this.disabled : this.parentData.disabled !== null ? this.parentData.disabled : false;
			},
			// 是否禁用label点击
			elLabelDisabled() {
				return this.labelDisabled !== '' ? this.labelDisabled : this.parentData.labelDisabled !== null ? this.parentData.labelDisabled :
					false;
			},
			// 组件尺寸,对应size的值,默认值为21px
			elSize() {
				return this.size ? this.size : (this.parentData.size ? this.parentData.size : 21);
			},
			// 组件的勾选图标的尺寸,默认12px
			elIconSize() {
				return this.iconSize ? this.iconSize : (this.parentData.iconSize ? this.parentData.iconSize : 12);
			},
			// 组件选中激活时的颜色
			elActiveColor() {
				return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : '#2979ff');
			},
			// 组件选未中激活时的颜色
			elInactiveColor() {
				return this.inactiveColor ? this.inactiveColor : (this.parentData.inactiveColor ? this.parentData.inactiveColor :
					'#c8c9cc');
			},
			// label的颜色
			elLabelColor() {
				return this.labelColor ? this.labelColor : (this.parentData.labelColor ? this.parentData.labelColor : '#606266')
			},
			// 组件的形状
			elShape() {
				return this.shape ? this.shape : (this.parentData.shape ? this.parentData.shape : 'circle');
			},
			// label大小
			elLabelSize() {
				return uni.$u.addUnit(this.labelSize ? this.labelSize : (this.parentData.labelSize ? this.parentData.labelSize :
					'15'))
			},
			elIconColor() {
				const iconColor = this.iconColor ? this.iconColor : (this.parentData.iconColor ? this.parentData.iconColor :
					'#ffffff');
				// 图标的颜色
				if (this.elDisabled) {
					// disabled状态下,已勾选的checkbox图标改为elInactiveColor
					return this.isChecked ? this.elInactiveColor : 'transparent'
				} else {
					return this.isChecked ? iconColor : 'transparent'
				}
			},
			iconClasses() {
				let classes = []
				// 组件的形状
				classes.push('u-checkbox__icon-wrap--' + this.elShape)
				if (this.elDisabled) {
					classes.push('u-checkbox__icon-wrap--disabled')
				}
				if (this.isChecked && this.elDisabled) {
					classes.push('u-checkbox__icon-wrap--disabled--checked')
				}
				// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
				// #ifdef MP-ALIPAY || MP-TOUTIAO
				classes = classes.join(' ')
				// #endif
				return classes
			},
			iconWrapStyle() {
				// checkbox的整体样式
				const style = {}
				style.backgroundColor = this.isChecked && !this.elDisabled ? this.elActiveColor : '#ffffff'
				style.borderColor = this.isChecked && !this.elDisabled ? this.elActiveColor : this.elInactiveColor
				style.width = uni.$u.addUnit(this.elSize)
				style.height = uni.$u.addUnit(this.elSize)
				// 如果是图标在右边的话,移除它的右边距
				if (this.parentData.iconPlacement === 'right') {
					style.marginRight = 0
				}
				return style
			},
			checkboxStyle() {
				const style = {}
				if (this.parentData.borderBottom && this.parentData.placement === 'row') {
					uni.$u.error('检测到您将borderBottom设置为true,需要同时将u-checkbox-group的placement设置为column才有效')
				}
				// 当父组件设置了显示下边框并且排列形式为纵向时,给内容和边框之间加上一定间隔
				if (this.parentData.borderBottom && this.parentData.placement === 'column') {
					style.paddingBottom = '8px'
				}
				return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
			}
		},
		mounted() {
			this.init()
		},
		methods: {
			init() {
				// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
				this.updateParentData()
				if (!this.parent) {
					// uni.$u.error('u-checkbox必须搭配u-checkbox-group组件使用')
				}
				// 设置初始化时,是否默认选中的状态,父组件u-checkbox-group的value可能是array,所以额外判断
				if (this.checked) {
					this.isChecked = true
				} else if (uni.$u.test.array(this.parentData.value)) {
					// 查找数组是是否存在this.name元素值
					this.isChecked = this.parentData.value.some(item => {
						return item === this.name
					})
				}
			},
			updateParentData() {
				this.getParentData('u-checkbox-group')
			},
			// 横向两端排列时,点击组件即可触发选中事件
			wrapperClickHandler(e) {
				this.parentData.iconPlacement === 'right' && this.iconClickHandler(e)
			},
			// 点击图标
			iconClickHandler(e) {
				this.preventEvent(e)
				// 如果整体被禁用,不允许被点击
				if (!this.elDisabled) {
					this.setRadioCheckedStatus()
				}
			},
			// 点击label
			labelClickHandler(e) {
				this.preventEvent(e)
				// 如果按钮整体被禁用或者label被禁用,则不允许点击文字修改状态
				if (!this.elLabelDisabled && !this.elDisabled) {
					this.setRadioCheckedStatus()
				}
			},
			emitEvent() {
				this.$emit('change', this.isChecked)
				// 尝试调用u-form的验证方法,进行一定延迟,否则微信小程序更新可能会不及时
				this.$nextTick(() => {
					uni.$u.formValidate(this, 'change')
				})
			},
			// 改变组件选中状态
			// 这里的改变的依据是,更改本组件的checked值为true,同时通过父组件遍历所有u-checkbox实例
			// 将本组件外的其他u-checkbox的checked都设置为false(都被取消选中状态),因而只剩下一个为选中状态
			setRadioCheckedStatus() {
				// 将本组件标记为与原来相反的状态
				this.isChecked = !this.isChecked
				this.emitEvent()
				typeof this.parent.unCheckedOther === 'function' && this.parent.unCheckedOther(this)
			}
		},
		watch:{
			checked(){
				this.isChecked = this.checked
			}
		}
	}
</script>

<style lang="scss" scoped>
	@import "../components.scss";
	$u-checkbox-icon-wrap-margin-right:6px !default;
	$u-checkbox-icon-wrap-font-size:6px !default;
	$u-checkbox-icon-wrap-border-width:1px !default;
	$u-checkbox-icon-wrap-border-color:#c8c9cc !default;
	$u-checkbox-icon-wrap-icon-line-height:0 !default;
	$u-checkbox-icon-wrap-circle-border-radius:100% !default;
	$u-checkbox-icon-wrap-square-border-radius:3px !default;
	$u-checkbox-icon-wrap-checked-color:#fff !default;
	$u-checkbox-icon-wrap-checked-background-color:red !default;
	$u-checkbox-icon-wrap-checked-border-color:#2979ff !default;
	$u-checkbox-icon-wrap-disabled-background-color:#ebedf0 !default;
	$u-checkbox-icon-wrap-disabled-checked-color:#c8c9cc !default;
	$u-checkbox-label-margin-left:5px !default;
	$u-checkbox-label-margin-right:12px !default;
	$u-checkbox-label-color:$u-content-color !default;
	$u-checkbox-label-font-size:15px !default;
	$u-checkbox-label-disabled-color:#c8c9cc !default;

	.u-checkbox {
		/* #ifndef APP-NVUE */
		@include flex(row);
		/* #endif */
		overflow: hidden;
		flex-direction: row;
		align-items: center;

		&-label--left {
			flex-direction: row
		}

		&-label--right {
			flex-direction: row-reverse;
			justify-content: space-between
		}

		&__icon-wrap {
			/* #ifndef APP-NVUE */
			box-sizing: border-box;
			// nvue下,border-color过渡有问题
			transition-property: border-color, background-color, color;
			transition-duration: 0.2s;
			/* #endif */
			color: $u-content-color;
			@include flex;
			align-items: center;
			justify-content: center;
			color: transparent;
			text-align: center;
			margin-right: $u-checkbox-icon-wrap-margin-right;

			font-size: $u-checkbox-icon-wrap-font-size;
			border-width: $u-checkbox-icon-wrap-border-width;
			border-color: $u-checkbox-icon-wrap-border-color;
			border-style: solid;

			/* #ifdef MP-TOUTIAO */
			// 头条小程序兼容性问题,需要设置行高为0,否则图标偏下
			&__icon {
				line-height: $u-checkbox-icon-wrap-icon-line-height;
			}

			/* #endif */

			&--circle {
				border-radius: $u-checkbox-icon-wrap-circle-border-radius;
			}

			&--square {
				border-radius: $u-checkbox-icon-wrap-square-border-radius;
			}

			&--checked {
				color: $u-checkbox-icon-wrap-checked-color;
				background-color: $u-checkbox-icon-wrap-checked-background-color;
				border-color: $u-checkbox-icon-wrap-checked-border-color;
			}

			&--disabled {
				background-color: $u-checkbox-icon-wrap-disabled-background-color !important;
			}

			&--disabled--checked {
				color: $u-checkbox-icon-wrap-disabled-checked-color !important;
			}
		}

		&__label {
			/* #ifndef APP-NVUE */
			word-wrap: break-word;
			/* #endif */
			margin-left: $u-checkbox-label-margin-left;
			margin-right: $u-checkbox-label-margin-right;
			color: $u-checkbox-label-color;
			font-size: $u-checkbox-label-font-size;

			&--disabled {
				color: $u-checkbox-label-disabled-color;
			}
		}
	}
</style>

  1. origin-component/custom-checkbox/props.js
  • 完全复制uview-ui的代码
export default {
    props: {
        // checkbox的名称
        name: {
            type: [String, Number, Boolean],
            default: uni.$u.props.checkbox.name
        },
        // 形状,square为方形,circle为圆型
        shape: {
            type: String,
            default: uni.$u.props.checkbox.shape
        },
        // 整体的大小
        size: {
            type: [String, Number],
            default: uni.$u.props.checkbox.size
        },
        // 是否默认选中
        checked: {
            type: Boolean,
            default: uni.$u.props.checkbox.checked
        },
        // 是否禁用
        disabled: {
            type: [String, Boolean],
            default: uni.$u.props.checkbox.disabled
        },
        // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
        activeColor: {
            type: String,
            default: uni.$u.props.checkbox.activeColor
        },
        // 未选中的颜色
        inactiveColor: {
            type: String,
            default: uni.$u.props.checkbox.inactiveColor
        },
        // 图标的大小,单位px
        iconSize: {
            type: [String, Number],
            default: uni.$u.props.checkbox.iconSize
        },
        // 图标颜色
        iconColor: {
            type: String,
            default: uni.$u.props.checkbox.iconColor
        },
        // label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式
        label: {
            type: [String, Number],
            default: uni.$u.props.checkbox.label
        },
        // label的字体大小,px单位
        labelSize: {
            type: [String, Number],
            default: uni.$u.props.checkbox.labelSize
        },
        // label的颜色
        labelColor: {
            type: String,
            default: uni.$u.props.checkbox.labelColor
        },
        // 是否禁止点击提示语选中复选框
        labelDisabled: {
            type: [String, Boolean],
            default: uni.$u.props.checkbox.labelDisabled
        }
    }
}

  1. origin-component/custom-picker/custom-picker.vue
  • 改造了picker-view-column标签下的代码,之前只是文字形式,现在让叶子节点也就是最后一集级以复选框的形式展示,leafLevel是外面传进来的叶子节点的层级
    在这里插入图片描述

  • 增加方法boxItemChange 监听checkbox的选择状态,并调用父组件的boxChange方法
    在这里插入图片描述

<template>
	<u-popup :show="show" @close="closeHandler">
		<view class="u-picker">
			<u-toolbar v-if="showToolbar" :cancelColor="cancelColor" :confirmColor="confirmColor"
				:cancelText="cancelText" :confirmText="confirmText" :title="title" :titleColor="titleColor"
				@cancel="cancel" @confirm="confirm"></u-toolbar>
			<picker-view class="u-picker__view" :indicatorStyle="`height: ${$u.addUnit(itemHeight)}`"
				:value="innerIndex" :immediateChange="immediateChange" :style="{
					height: `${$u.addUnit(visibleItemCount * itemHeight)}`
				}" @change="changeHandler">
				<picker-view-column v-for="(item, index) in innerColumns" :key="index" class="u-picker__view__column">
					<block v-if="index != leafLevel">
						<text v-if="$u.test.array(item)" class="u-picker__view__column__item u-line-1"
							:class="item1.class" v-for="(item1, index1) in item" :key="index1" :style="{
							height: $u.addUnit(itemHeight),
							lineHeight: $u.addUnit(itemHeight),
							fontWeight: index1 === innerIndex[index] ? 'bold' : 'normal'
						}">{{ getItemText(item1) }}</text>
					</block>
					<block v-else>
						<custom-checkbox class="u-line-1 checkbox-container" :customStyle="{display:'flex !important'}"
							v-for="(item1, index1) in item" :key="item1.label" :label="item1.label" :name="item1.label" :checked="isBoxChecked(item1)"
							@change="boxItemChange(item1,$event)">
						</custom-checkbox>
					</block>
				</picker-view-column>
			</picker-view>
			<view v-if="loading" class="u-picker--loading">
				<u-loading-icon mode="circle"></u-loading-icon>
			</view>
		</view>
	</u-popup>
</template>

<script>
	/**
	 * u-picker
	 * @description 选择器
	 * @property {Boolean}			show				是否显示picker弹窗(默认 false )
	 * @property {Boolean}			showToolbar			是否显示顶部的操作栏(默认 true )
	 * @property {String}			title				顶部标题
	 * @property {Array}			columns				对象数组,设置每一列的数据
	 * @property {Boolean}			loading				是否显示加载中状态(默认 false )
	 * @property {String | Number}	itemHeight			各列中,单个选项的高度(默认 44 )
	 * @property {String}			cancelText			取消按钮的文字(默认 '取消' )
	 * @property {String}			confirmText			确认按钮的文字(默认 '确定' )
	 * @property {String}			cancelColor			取消按钮的颜色(默认 '#909193' )
	 * @property {String}			confirmColor		确认按钮的颜色(默认 '#3c9cff' )
	 * @property {String | Number}	visibleItemCount	每列中可见选项的数量(默认 5 )
	 * @property {String}			keyName				选项对象中,需要展示的属性键名(默认 'text' )
	 * @property {Boolean}			closeOnClickOverlay	是否允许点击遮罩关闭选择器(默认 false )
	 * @property {Array}			defaultIndex		各列的默认索引
	 * @property {Boolean}			immediateChange		是否在手指松开时立即触发change事件(默认 false )
	 * @property {Number}			leafLevel 叶子节点的层级
	 * @event {Function} close		关闭选择器时触发
	 * @event {Function} cancel		点击取消按钮触发
	 * @event {Function} change		当选择值变化时触发
	 * @event {Function} boxChange		当最后一级多选框值变化时触发
	 * @event {Function} confirm	点击确定按钮,返回当前选择的值
	 */
	import props from './props.js';
	import CustomCheckbox from '../custom-checkbox/custom-checkbox.vue'
	export default {
		name: 'custom-picker',
		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
		components:{
			CustomCheckbox
		},
		data() {
			return {
				// 上一次选择的列索引
				lastIndex: [],
				// 索引值 ,对应picker-view的value
				innerIndex: [],
				// 各列的值
				innerColumns: [],
				// 上一次的变化列索引
				columnIndex: 0,
				checkedBoxs: []
			}
		},
		watch: {
			// 监听默认索引的变化,重新设置对应的值
			defaultIndex: {
				immediate: true,
				handler(n) {
					this.setIndexs(n, true)
				}
			},
			// 监听columns参数的变化
			columns: {
				immediate: true,
				handler(n) {
					this.setColumns(n)
				}
			},
		},
		methods: {
			isBoxChecked(item){
				return this.checkedBoxs.includes(item)
			},
			// 获取item需要显示的文字,判别为对象还是文本
			getItemText(item) {
				if (uni.$u.test.object(item)) {
					return item[this.keyName]
				} else {
					return item
				}
			},
			// 关闭选择器
			closeHandler() {
				if (this.closeOnClickOverlay) {
					this.$emit('close')
				}
			},
			// 点击工具栏的取消按钮
			cancel() {
				this.$emit('cancel')
			},
			// 点击工具栏的确定按钮
			confirm() {
				this.$emit('confirm', {
					indexs: this.innerIndex,
					value: this.innerColumns.map((item, index) => item[this.innerIndex[index]]),
					values: this.innerColumns,
					checkedBoxs: this.checkedBoxs
				})
			},
			// 选择器某一列的数据发生变化时触发
			changeHandler(e) {
				const {
					value
				} = e.detail
				let index = 0,
					columnIndex = 0
				// 通过对比前后两次的列索引,得出当前变化的是哪一列
				for (let i = 0; i < value.length; i++) {
					let item = value[i]
					if (item !== (this.lastIndex[i] || 0)) { // 把undefined转为合法假值0
						// 设置columnIndex为当前变化列的索引
						columnIndex = i
						// index则为变化列中的变化项的索引
						index = item
						break // 终止循环,即使少一次循环,也是性能的提升
					}
				}
				this.columnIndex = columnIndex
				const values = this.innerColumns
				// 将当前的各项变化索引,设置为"上一次"的索引变化值
				this.setLastIndex(value)
				this.setIndexs(value)

				this.$emit('change', {
					// #ifndef MP-WEIXIN || MP-LARK
					// 微信小程序不能传递this,会因为循环引用而报错
					picker: this,
					// #endif
					value: this.innerColumns.map((item, index) => item[value[index]]),
					index,
					indexs: value,
					// values为当前变化列的数组内容
					values,
					columnIndex,
					detail: this.lastIndex
				})
			},
			// 设置index索引,此方法可被外部调用设置
			setIndexs(index, setLastIndex) {
				this.innerIndex = uni.$u.deepClone(index)
				if (setLastIndex) {
					this.setLastIndex(index)
				}
			},
			// 记录上一次的各列索引位置
			setLastIndex(index) {
				// 当能进入此方法,意味着当前设置的各列默认索引,即为“上一次”的选中值,需要记录,是因为changeHandler中
				// 需要拿前后的变化值进行对比,得出当前发生改变的是哪一列
				this.lastIndex = uni.$u.deepClone(index)
			},
			// 设置对应列选项的所有值
			setColumnValues(columnIndex, values) {
				// 替换innerColumns数组中columnIndex索引的值为values,使用的是数组的splice方法
				this.innerColumns.splice(columnIndex, 1, values)
				// 拷贝一份原有的innerIndex做临时变量,将大于当前变化列的所有的列的默认索引设置为0
				let tmpIndex = uni.$u.deepClone(this.innerIndex)
				for (let i = 0; i < this.innerColumns.length; i++) {
					if (i > this.columnIndex) {
						tmpIndex[i] = 0
					}
				}
				// 一次性赋值,不能单个修改,否则无效
				this.setIndexs(tmpIndex)
			},
			// 获取对应列的所有选项
			getColumnValues(columnIndex) {
				// 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值
				// 索引如果在外部change的回调中调用getColumnValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性
				(async () => {
					await uni.$u.sleep()
				})()
				return this.innerColumns[columnIndex]
			},
			// 设置整体各列的columns的值
			setColumns(columns) {
				this.innerColumns = uni.$u.deepClone(columns)
				// 如果在设置各列数据时,没有被设置默认的各列索引defaultIndex,那么用0去填充它,数组长度为列的数量
				if (this.innerIndex.length === 0) {
					this.innerIndex = new Array(columns.length).fill(0)
				}
			},
			// 获取各列选中值对应的索引
			getIndexs() {
				return this.innerIndex
			},
			// 获取各列选中的值
			getValues() {
				// 进行同步阻塞,因为外部得到change事件之后,可能需要执行setColumnValues更新列的值
				// 索引如果在外部change的回调中调用getValues的话,可能无法得到变更后的列值,这里进行一定延时,保证值的准确性
				(async () => {
					await uni.$u.sleep()
				})()
				return this.innerColumns.map((item, index) => item[this.innerIndex[index]])
			},
			boxItemChange(data,event) {
				if(event){
					this.checkedBoxs.push(data)
				}else{
					this.checkedBoxs.splice(this.checkedBoxs.indexOf(data),1);
				}
				this.$emit('boxChange', this.checkedBoxs)
				// console.log(this.checkedBoxs)
			}
		},
	}
</script>

<style lang="scss" scoped>
	@import "../components.scss";

	.u-picker {
		position: relative;

		&__view {

			&__column {
				@include flex;
				flex: 1;
				justify-content: center;

				&__item {
					@include flex;
					justify-content: center;
					align-items: center;
					font-size: 16px;
					text-align: center;
					/* #ifndef APP-NVUE */
					display: block;
					/* #endif */
					color: $u-main-color;

					&--disabled {
						/* #ifndef APP-NVUE */
						cursor: not-allowed;
						/* #endif */
						opacity: 0.35;
					}
				}
			}
		}

		&--loading {
			position: absolute;
			top: 0;
			right: 0;
			left: 0;
			bottom: 0;
			@include flex;
			justify-content: center;
			align-items: center;
			background-color: rgba(255, 255, 255, 0.87);
			z-index: 1000;
		}
	}
	.checkbox-container{
		justify-content: center;
		align-items: center;
	}
</style>

  1. origin-component/custom-picker/props.js
  • 复制uview-ui的代码,加了一个leafLevel属性
export default {
	props: {
		// 是否展示picker弹窗
		show: {
			type: Boolean,
			default: uni.$u.props.picker.show
		},
		// 是否展示顶部的操作栏
		showToolbar: {
			type: Boolean,
			default: uni.$u.props.picker.showToolbar
		},
		// 顶部标题
		title: {
			type: String,
			default: uni.$u.props.picker.title
		},
		// 对象数组,设置每一列的数据
		columns: {
			type: Array,
			default: uni.$u.props.picker.columns
		},
		titleColor: {
			type: String,
			default:"#1e1e1e"
		},
		// 是否显示加载中状态
		loading: {
			type: Boolean,
			default: uni.$u.props.picker.loading
		},
		// 各列中,单个选项的高度
		itemHeight: {
			type: [String, Number],
			default: uni.$u.props.picker.itemHeight
		},
		// 取消按钮的文字
		cancelText: {
			type: String,
			default: uni.$u.props.picker.cancelText
		},
		// 确认按钮的文字
		confirmText: {
			type: String,
			default: uni.$u.props.picker.confirmText
		},
		// 取消按钮的颜色
		cancelColor: {
			type: String,
			default: uni.$u.props.picker.cancelColor
		},
		// 确认按钮的颜色
		confirmColor: {
			type: String,
			default: uni.$u.props.picker.confirmColor
		},
		// 每列中可见选项的数量
		visibleItemCount: {
			type: [String, Number],
			default: uni.$u.props.picker.visibleItemCount
		},
		// 选项对象中,需要展示的属性键名
		keyName: {
			type: String,
			default: uni.$u.props.picker.keyName
		},
		// 是否允许点击遮罩关闭选择器
		closeOnClickOverlay: {
			type: Boolean,
			default: uni.$u.props.picker.closeOnClickOverlay
		},
		// 各列的默认索引
		defaultIndex: {
			type: Array,
			default: uni.$u.props.picker.defaultIndex
		},
		// 是否在手指松开时立即触发 change 事件。若不开启则会在滚动动画结束后触发 change 事件,只在微信2.21.1及以上有效
		immediateChange: {
			type: Boolean,
			default: uni.$u.props.picker.immediateChange
		},
		leafLevel:{
			type: Number,
			default: 0
		}
	}
}

  1. origin-component/components.scss
  • 完全复制uview-ui的代码
@import "./mixin.scss";

/* #ifndef APP-NVUE */
// 由于uView是基于nvue环境进行开发的,此环境中普通元素默认为flex-direction: column;
// 所以在非nvue中,需要对元素进行重置为flex-direction: column; 否则可能会表现异常
view, scroll-view, swiper-item {
	display: flex;
	flex-direction: column;
	flex-shrink: 0;
	flex-grow: 0;
	flex-basis: auto;
	align-items: stretch;
	align-content: flex-start;
}
/* #endif */

  1. origin-component/mixin.scss
  • 完全复制uview-ui的代码
// 通过scss的mixin功能,把原来需要写4行的css,变成一行
// 目的是保持代码干净整洁,不至于在nvue下,到处都要写display:flex的条件编译
@mixin flex($direction: row) {
	/* #ifndef APP-NVUE */
	display: flex;
	/* #endif */
	flex-direction: $direction;
}

  1. constants.js
    picker内容的一些数据
const factorStructure1 = [
  {
    label: "基础参数",
    value: 1,
    level:0,
    icon:'base_params.png',
    children: [
      {
        label: "电量",
        value: 1,
        level:1,
        children: [
          {
            label: "A相电流",
            value: 12,
            level:2
          },
          {
            label: "B相电流",
            value: 13,
            level:2
          },
          {
            label: "C相电流",
            value: 14,
            level:2
          }
        ],
      },
      {
        label: "电压",
        value: 2,
        level:1,
        children: [
          {
            label: "A相电压",
            value: 9,
            level:2
          },
          {
            label: "B相电压",
            value: 10,
            level:2
          },
          {
            label: "C相电压",
            value: 11,
            level:2
          }
        ],
      },
      {
        label: "有功功率",
        value: 3,
        level:1,
        children: [
          {
            label: "总有功功率",
            value: 18
          },
          {
            label: "有功功率1",
            value: 19
          },
          {
            label: "有功功率2",
            value: 20
          },
          {
            label: "有功功率3",
            value: 21
          }
        ],
      },
      {
        label: "无功功率",
        value: 4,
        level:1,
        children: [
          {
            label: "无有功功率",
            value: 22,
            level:2
          },
          {
            label: "无功功率1",
            value: 23,
            level:2
          },
          {
            label: "无功功率2",
            value: 24,
            level:2
          },
          {
            label: "无功功率3",
            value: 25,
            level:2
          }
        ],
      },
      {
        label: "功率因素",
        value: 5,
        level:1,
        children: [
          {
            label: "总功率因素",
            value: 26,
            level:2
          },
          {
            label: "功率因素1",
            value: 27,
            level:2
          },
          {
            label: "功率因素2",
            value: 28,
            level:2
          },
          {
            label: "功率因素3",
            value: 29,
            level:2
          }
        ],
      },
      {
        label: "电网频率",
        value: 6,
        level:1,
        children: [
          {
            label: "电网频率",
            value: 15,
            level:2
          }
        ],
      },
    ],
  },
  {
    label: "电量",
    value: 2,
    level:0,
    icon:'electricity.png',
    children: [
      {
        label: "有功电量",
        value: 1,
        level:1,
        children: [
          {
            label: "正向有功总电能",
            value: 1,
            level:2
          },
          {
            label: "反向有功总电能",
            value: 2,
            level:2
          }
        ],
      },
      {
        label: "无功电量",
        value: 2,
        level:1,
        children: [
          {
            label: "组合无功1总电能",
            value: 3,
            level:2
          },
          {
            label: "组合无功2总电能",
            value: 4,
            level:2
          },
          {
            label: "(当前)第一象限无功总电能",
            value: 5,
            level:2
          },
          {
            label: "(当前)第二象限无功总电能",
            value: 6,
            level:2
          },
          {
            label: "(当前)第三象限无功总电能",
            value: 7,
            level:2
          },
          {
            label: "(当前)第四象限无功总电能",
            value: 8,
            level:2
          },
        ],
      },
      {
        label: "需量",
        value: 3,
        level:1,
        children: [
          {
            label: "当前有功需量",
            value: 16,
            level:2
          },
          {
            label: "当前无功需量",
            value: 17,
            level:2
          },
        ],
      },
    ],
  },
]
export {
  factorStructure1
}
  1. index.vue
<template>
	<view class="container">
		<custom-picker :closeOnClickOverlay="false" :show="isShow" :columns="columns" :title="title || '请选择因子'" :loading="loading" :leafLevel="2"
			keyName="label" titleColor="#ff5757" ref="factorPicker" @confirm="confirm" @change="changeHandler" @boxChange="boxChange" @cancel="cancel">
		</custom-picker>
	</view>
</template>

<script>
	import {
		factorStructure1
	} from "./constant";
	import CustomPicker from './origin-component/custom-picker/custom-picker.vue'
	export default {
		components:{
			CustomPicker
		},
		props: {},
		data() {
			return {
				loading: false,
				isShow: false,
				columns: [factorStructure1,factorStructure1[0].children,factorStructure1[0].children[0].children],
				title: '',
				preSelectedData:[] // 记录上次选择的结果,方便取消的时候回滚数据
			};
		},
		watch: {
			isShow(val) {
				if (!val) {
					this.$emit('close')
				}
			}
		},
		methods: {
			show() {
				this.isShow = true
				// const temp = {
				// 		label: "请选择",
				// 		id: -1,
				// 		hasChildren: false,
				// 		class: "test-select"
				// 	}
				
				// this.columns.forEach(item=>{
				// 	item.splice(0, 0, temp)
				// })
				// if (this.$refs.factorPicker.innerColumns.length === 0) {
					// this.loading = true
					// // this.loadData()
					// const temp = [{
					// 		label: "请选择",
					// 		id: -1,
					// 		hasChildren: false,
					// 		class: "test-select"
					// 	},
					// ]
					// this.$refs.factorPicker.setColumnValues(0, temp)
					// this.columns.splice(0, 1, temp)
					// this.loading = false
				// } else {
				// 	if (this.reset) {
				// 		this.$refs.factorPicker.innerColumns.splice(1)
				// 		this.$refs.factorPicker.innerIndex = [0]
				// 		this.$refs.factorPicker.setIndexs([0], true)
				// 	}
				// }
				
			},
			confirm(e) {
				this.isShow = false
				let data = e.checkedBoxs
				this.changeTitle(data)
				this.preSelectedData = []
				data.forEach(item=>{
					this.preSelectedData.push(item)
				})
				this.$emit("factorsChange",this.title,data)
				console.log("确定了")
			},
			cancel(){
				this.isShow = false;
				let temp = []
				this.preSelectedData.forEach(item=>{
					temp.push(item)
				})
				this.$refs.factorPicker.checkedBoxs = temp
				this.changeTitle(temp)
			},
			changeHandler(e) {
				const {
					columnIndex,
					value,
					values, // values为当前变化列的数组内容
					index,
					// 微信小程序无法将picker实例传出来,只能通过ref操作
					picker = this.$refs.factorPicker
				} = e
				// console.log(e)
				
				// this.columns.splice()
				if(columnIndex < 2){
					picker.setColumnValues(columnIndex+1, values[columnIndex][index].children)
					for(let i = columnIndex+1;i < 2; i++){
						picker.setColumnValues(i+1, values[i][0].children)
					}
				}
			},
			boxChange(data){
				this.changeTitle(data)
			},
			changeTitle(data){
				let result = ''
				if(data.length == 0){
					result = ''
				}else if(data.length == 1){
					result = data[0].label
				}else{
					result = `选择了${data.length}个因子`
				}
				this.title = result
			}
		}
	}
</script>

<style lang="scss">
	
</style>

  1. example.vue
  • 使用方法
<template>
	<view class="container">
		<u--input v-model="factorsSelectedResult" placeholder="请选择因子" suffixIcon="arrow-down"
			readonly suffixIconStyle="color: #909399" @click.native.stop='showFactorSelector'>
		</u--input>
		<vr-factor-selector ref='factorSelector' @factorsChange="factorsChange"></vr-factor-selector>
	</view>
</template>
<script>
	import VrFactorSelector from './index.vue'
	export default {
		components: {
			VrFactorSelector
		},
		data(){
			return{
				factorsSelectedResult:''
			}
		},
		methods: {
			showFactorSelector() {
				this.$refs.factorSelector.show()
			},
			factorsChange(result,data){
				console.log(result,data)
				this.factorsSelectedResult = result
			}
		}
	}
</script>
<style>

</style>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值