170 lines
3.7 KiB
Vue
Raw Normal View History

2026-04-07 16:02:19 +08:00
<template>
<div class="base-picker">
<!-- 使用 van-field 作为展示区域 -->
<van-field
:modelValue="displayValue"
:label="label"
2026-04-24 16:51:36 +08:00
:required="required"
2026-04-07 16:02:19 +08:00
:placeholder="placeholder"
:disabled="disabled"
:readonly="true"
:right-icon="rightIcon"
:clickable="!disabled"
@click="openPicker"
/>
<!-- 弹出层选择器 -->
<van-popup v-model:show="showPicker" position="bottom" round>
<van-picker
2026-04-15 09:08:41 +08:00
:modelValue="getPickerValue()"
2026-04-07 16:02:19 +08:00
: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: {
2026-04-08 11:12:41 +08:00
type: [String, Number, Boolean],
2026-04-07 16:02:19 +08:00
default: null
},
// 选项数据,默认格式 [{ label: '显示名', value: '值' }]
options: {
type: Array,
required: true,
default: () => []
},
// 左侧标签文字
label: {
type: String,
default: ''
},
2026-04-24 16:51:36 +08:00
required: {
type: Boolean,
default: false
},
2026-04-07 16:02:19 +08:00
// 占位符
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)
2026-04-15 09:08:41 +08:00
const getPickerValue = () => {
// 布尔值需要转换为数字交给picker, 在设置的时候,又需要转换回来
if(props.modelValue === true) return [1]
if(props.modelValue === false) return [0]
return [props.modelValue]
}
2026-04-07 16:02:19 +08:00
// 处理 Picker 数据格式:将 options 转为 Picker 需要的文本数组
const columns = computed(() => {
return props.options.map(item => {
2026-04-15 09:08:41 +08:00
let value = item[props.valueKey]
let isBoolean = false
// vant不支持布尔值需要做转换
if(value === true) {
value = 1
isBoolean = true
}
if(value === false) {
value = 0
isBoolean = true
}
2026-04-07 16:02:19 +08:00
return {
text: item[props.labelKey],
2026-04-15 09:08:41 +08:00
value,
isBoolean
2026-04-07 16:02:19 +08:00
}
})
})
// 根据选中的值,获取显示文本
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 }) => {
2026-04-15 09:08:41 +08:00
let value = selectedOptions[0][props.valueKey]
// 如果是布尔值,需要转换为布尔值
if(selectedOptions[0].isBoolean) {
value = value === 1 ? true : false
}
2026-04-07 16:02:19 +08:00
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;
}
2026-04-24 16:51:36 +08:00
</style>