|
|
<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" @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" > <text v-if="$u.test.array(item)" class="u-picker__view__column__item u-line-1" 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> </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 ) * @event {Function} close 关闭选择器时触发 * @event {Function} cancel 点击取消按钮触发 * @event {Function} change 当选择值变化时触发 * @event {Function} confirm 点击确定按钮,返回当前选择的值 */ import props from './props.js'; export default { name: 'u-picker', mixins: [uni.$u.mpMixin, uni.$u.mixin, props], data() { return { // 上一次选择的列索引
lastIndex: [], // 索引值 ,对应picker-view的value
innerIndex: [], // 各列的值
innerColumns: [], // 上一次的变化列索引
columnIndex: 0, } }, watch: { // 监听默认索引的变化,重新设置对应的值
defaultIndex: { immediate: true, handler(n) { this.setIndexs(n, true) } }, // 监听columns参数的变化
columns: { immediate: true, handler(n) { this.setColumns(n) } }, }, methods: { // 获取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 }) }, // 选择器某一列的数据发生变化时触发
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 }) }, // 设置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]]) } }, } </script>
<style lang="scss" scoped> @import "../../libs/css/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; } } </style>
|