<template>
<view class="mask_box" v-if="!insert && needMask && visible" @click="onCancel"></view>
<view class="custom-box"><slot></slot></view>
<view
:class="{ 'custom-picker-container': !insert, 'custom-picker': insert }"
:style="customPickerContainer"
v-if="visible"
>
<view class="picker-header" v-if="needBtn">
<text class="picker-cancel" @click="onCancel">{{ cancelText }}</text>
<text v-if="title" class="picker-title">{{ title }}</text>
<text class="picker-confirm" @click="onConfirm">{{ confirmText }}</text>
</view>
<picker-view class="picker-view" :value="pickerValue" @change="onChange">
<picker-view-column v-for="(column, columnIndex) in columns" :key="columnIndex">
<view
class="picker-item"
v-for="(item, index) in column"
:key="index"
:class="{ 'picker-item-selected': isSelected(columnIndex, index) }"
>
{{ item }}
</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
/**
* ZyPicker 选择器组件
* @property {Boolean} visible 展示状态
* @property {String} title 标题文本信息,不传则不展示
* @property {String} cancelText 取消按钮文案
* @property {String} confirmText 确认按钮文案
* @property {Array} columns 数据,需要几列数据则传入几列数组数据即可
* @property {Array} initialValue 初始选中值的索引数组
* @property {Boolean} insert = [true|false] 插入模式,默认为true
* @property {Boolean} needBtn = [true|false] 是否需要按钮,默认为true
* @property {Boolean} needMask = [true|false] 非插入模式下是否需要蒙层,默认为false
* @property {Object} customPickerContainer = 组件样式传参
* @event {Function} cancel 触发取消事件
* @event {Function} onConfirm 触发确认事件并传递选中的值
**/
export default {
name: 'ZyPicker',
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: ''
},
cancelText: {
type: String,
default: '取消'
},
confirmText: {
type: String,
default: '确定'
},
columns: {
type: Array,
default: () => [[]]
},
initialValue: {
type: Array, // 初始选中值的索引数组
default: () => []
},
insert: {
type: Boolean,
default: true
},
needBtn: {
type: Boolean,
default: true
},
needMask: {
type: Boolean,
default: false
},
customPickerContainer: {
type: Object,
default: () => {}
}
},
data() {
return {
pickerValue: this.getInitialPickerValue() // 初始化选中值
}
},
watch: {
// 监听initialValue的变化并更新pickerValue
initialValue(newVal) {
this.pickerValue = newVal
}
},
methods: {
getInitialPickerValue() {
// 确保每列都有初始值,未提供的列使用默认值(索引为0)
return this.columns.map((_, index) => this.initialValue[index] ?? 0)
},
// onChange(e) {
// this.pickerValue = e.detail.value // 更新当前选中值
// const selectedValues = this.columns.map((column, index) => column[this.pickerValue[index]])
// this.$emit('change', selectedValues) // 触发确认事件并传递选中的值
// },
onChange(e) {
console.log('🚀 ~ onChange ~ this.column:', this.column)
console.log('🚀 ~ onChange ~ e:', e)
const newValue = e.detail.value.map((val, index) => {
const maxIndex = this.columns[index].length - 1
if (val < 0 || val > maxIndex) {
// 如果超出范围,自动定位到最后一个有效项
return maxIndex
}
return val
})
this.pickerValue = newValue
// 计算最后一列的滚动位置(假设只有一列或多列统一滚动)
const selectedIndex = this.pickerValue[this.pickerValue.length - 1]
this.pickerScrollTop = selectedIndex * this.itemHeight
const selectedValues = this.columns.map((column, index) => column[this.pickerValue[index]])
this.$emit('change', selectedValues)
},
onCancel() {
this.$emit('cancel') // 触发取消事件
},
onConfirm() {
const selectedValues = this.columns.map((column, index) => column[this.pickerValue[index]])
this.$emit('confirm', selectedValues) // 触发确认事件并传递选中的值
},
isSelected(columnIndex, index) {
return this.pickerValue[columnIndex] === index // 判断是否为当前选中项
}
}
}
</script>
<style lang="scss">
.mask_box {
width: 100vw;
height: 100vh;
display: flex; /* 启用 Flexbox 布局 */
flex-direction: column; /* 设置主轴方向为竖直方向 */
flex-wrap: wrap; /* 允许子元素换行 */
justify-content: start;
position: fixed;
right: 0px;
left: 0px;
bottom: 0px;
z-index: 100;
background-color: $uni-mask-0_55;
}
.custom-box {
position: relative;
z-index: 66;
}
/* 插入样式 */
.custom-picker {
width: 100vw;
position: absolute;
top: 1;
left: 0;
background-color: $uni-bg-color-inverse;
z-index: 999;
border-radius: 8px 8px 0px 0;
// box-shadow: 10px 0 40px 0 rgba(113, 113, 113, 0.5);
box-shadow: 0 15px 40px -15px rgba(113, 113, 113, 0.5);
}
/* 弹窗样式 */
.custom-picker-container {
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
background-color: $uni-bg-color-inverse;
z-index: 999;
border-radius: 8px 8px 0px 0;
}
.picker-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
height: 30px;
border-bottom: 1px solid #eaeaea;
}
.picker-cancel,
.picker-confirm {
color: #007aff;
font-size: $uni-font-size-15;
}
.picker-title {
font-size: $uni-font-size-15;
color: $uni-text-color;
}
.picker-view {
height: 258px;
}
.picker-item {
display: flex;
align-items: center;
justify-content: center;
height: 50px;
font-size: 21px;
color: $uni-text-color;
text-align: center;
}
.picker-item-selected {
font-size: 21px;
transform: scale(1); /* 选中项保持原大小 */
opacity: 1;
}
.picker-item:not(.picker-item-selected) {
transform: scale(0.9); /* 非选中项缩小 */
opacity: 0.8; /* 非选中项透明度降低 */
}
</style>
当前组件可以会出现值变化了但是未自动定位到当前选中的值