164 lines
3.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="base-picker">
<!-- 使用 van-field 作为展示区域 -->
<van-field
:modelValue="displayValue"
:label="label"
:placeholder="placeholder"
:disabled="disabled"
:readonly="true"
:right-icon="rightIcon"
:clickable="!disabled"
@click="openPicker"
/>
<!-- 弹出层选择器 -->
<van-popup v-model:show="showPicker" position="bottom" round>
<van-picker
:modelValue="getPickerValue()"
:columns="columns"
:title="pickerTitle"
:loading="loading"
show-toolbar
@confirm="onConfirm"
@cancel="showPicker = false"
/>
</van-popup>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { Field, Popup, Picker } from 'vant'
// Props 定义
const props = defineProps({
// 双向绑定值 (v-model)
modelValue: {
type: [String, Number, Boolean],
default: null
},
// 选项数据,默认格式 [{ label: '显示名', value: '值' }]
options: {
type: Array,
required: true,
default: () => []
},
// 左侧标签文字
label: {
type: String,
default: ''
},
// 占位符
placeholder: {
type: String,
default: '请选择'
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
// Picker 顶部标题
pickerTitle: {
type: String,
default: '请选择'
},
// 加载状态
loading: {
type: Boolean,
default: false
},
// 右侧图标(可自定义)
rightIcon: {
type: String,
default: 'arrow-down'
},
// 自定义选项的 label 字段名
labelKey: {
type: String,
default: 'label'
},
// 自定义选项的 value 字段名
valueKey: {
type: String,
default: 'value'
}
})
// Emits 定义
const emit = defineEmits(['update:modelValue', 'change'])
// 控制弹出层显示
const showPicker = ref(false)
const getPickerValue = () => {
// 布尔值需要转换为数字交给picker, 在设置的时候,又需要转换回来
if(props.modelValue === true) return [1]
if(props.modelValue === false) return [0]
return [props.modelValue]
}
// 处理 Picker 数据格式:将 options 转为 Picker 需要的文本数组
const columns = computed(() => {
return props.options.map(item => {
let value = item[props.valueKey]
let isBoolean = false
// vant不支持布尔值需要做转换
if(value === true) {
value = 1
isBoolean = true
}
if(value === false) {
value = 0
isBoolean = true
}
return {
text: item[props.labelKey],
value,
isBoolean
}
})
})
// 根据选中的值,获取显示文本
const displayValue = computed(() => {
if (props.modelValue === null || props.modelValue === undefined || props.modelValue === '') {
return ''
}
const selected = props.options.find(item => item[props.valueKey] === props.modelValue)
return selected ? selected[props.labelKey] : ''
})
// 打开选择器
const openPicker = () => {
if (!props.disabled) {
showPicker.value = true
}
}
// 确认选择
const onConfirm = ({ selectedValues, selectedOptions }) => {
let value = selectedOptions[0][props.valueKey]
// 如果是布尔值,需要转换为布尔值
if(selectedOptions[0].isBoolean) {
value = value === 1 ? true : false
}
const label = selectedOptions[0][props.labelKey]
emit('update:modelValue', value)
emit('change', { value, label })
showPicker.value = false
}
</script>
<style scoped>
.base-picker {
width: 100%;
}
/* 可选:调整 Field 的只读样式 */
:deep(.van-field__control--readonly) {
cursor: pointer;
}
</style>