SelectPicker.vue 4.2 KB
<template>
  <div>
    <!-- 标签 -->
    <div v-if="label" class="text-sm text-gray-600 mb-2 flex items-center">
      <span v-if="required" class="text-red-500 mr-1">*</span>
      <span>{{ label }}</span>
    </div>

    <!-- 触发区域 -->
    <div
      class="flex justify-between items-center border border-gray-200 rounded-lg p-3 bg-gray-50"
      @tap="openPicker"
    >
      <span :class="displayValue ? 'text-gray-900' : 'text-gray-400'" class="text-sm">
        {{ displayValue || placeholder }}
      </span>
      <IconFont name="right" size="14" color="#9CA3AF" />
    </div>

    <!-- Picker 弹窗 -->
    <nut-popup
      position="bottom"
      v-model:visible="showPicker"
      :z-index="9999"
      :overlay="true"
      :close-on-click-overlay="false"
    >
      <nut-picker
        :columns="pickerColumns"
        @confirm="onConfirm"
        @cancel="onCancel"
      />
    </nut-popup>
  </div>
</template>

<script setup>
/**
 * 下拉选择器组件
 *
 * @description 使用 NutUI Picker 实现下拉选择功能
 *              - key 和 value 相同(如"整付(0-75 岁)")
 *              - 适用于缴费年期等场景
 * @author Claude Code
 * @example
 * <SelectPicker
 *   v-model="paymentPeriod"
 *   label="缴费年期"
 *   placeholder="请选择缴费年期"
 *   :options="['整付(0-75 岁)', '5 年(0-70 岁)']"
 * />
 */
import { ref, computed, inject } from 'vue'
import IconFont from '@/components/IconFont.vue'

// 注入父组件提供的弹窗控制函数
const popupControl = inject('popupControl', null)

/**
 * 组件属性
 */
const props = defineProps({
  /**
   * 标签文本
   * @type {string}
   */
  label: {
    type: String,
    default: ''
  },

  /**
   * 是否必填
   * @type {boolean}
   */
  required: {
    type: Boolean,
    default: false
  },

  /**
   * 占位符文本
   * @type {string}
   */
  placeholder: {
    type: String,
    default: '请选择'
  },

  /**
   * 绑定的值
   * @type {string}
   */
  modelValue: {
    type: String,
    default: ''
  },

  /**
   * 选项数组(key 和 value 相同)
   * @type {Array<string>}
   * @example ['整付(0-75 岁)', '5 年(0-70 岁)', '10 年(0-70 岁)']
   */
  options: {
    type: Array,
    required: true
  }
})

/**
 * 组件事件
 */
const emit = defineEmits([
  /**
   * 更新值事件
   * @event update:modelValue
   * @param {string} value - 选中的选项
   */
  'update:modelValue',
  /**
   * 弹窗打开事件
   * @event open
   */
  'open',
  /**
   * 弹窗关闭事件
   * @event close
   */
  'close'
])

/**
 * 控制 Picker 显示
 */
const showPicker = ref(false)

/**
 * 打开选择器
 */
const openPicker = () => {
  // 调用父组件提供的 open 函数
  if (popupControl && popupControl.open) {
    popupControl.open()
  }

  showPicker.value = true
}

/**
 * 转换为 Picker 格式
 * @description 将选项数组转换为 Picker 需要的格式
 * @example
 * // options = ['整付(0-75 岁)', '5 年(0-70 岁)']
 * // pickerColumns() // 返回: [{ text: '整付(0-75 岁)', value: '整付(0-75 岁)' }, ...]
 */
const pickerColumns = computed(() => {
  return props.options.map(option => ({
    text: option,
    value: option // key 和 value 相同
  }))
})

/**
 * 显示的值
 */
const displayValue = computed(() => {
  return props.modelValue || ''
})

/**
 * 确认选择
 * @param {Object} params - Picker 返回参数
 * @param {Array} params.selectedOptions - 选中的选项数组
 *
 * @example
 * // 用户选择 '整付(0-75 岁)'
 * onConfirm({ selectedOptions: [{ text: '整付(0-75 岁)', value: '整付(0-75 岁)' }] })
 * // -> emit('update:modelValue', '整付(0-75 岁)')
 */
const onConfirm = ({ selectedOptions }) => {
  const value = selectedOptions[0]?.value
  if (value !== undefined) {
    emit('update:modelValue', value)
  }

  // 调用父组件提供的 close 函数
  if (popupControl && popupControl.close) {
    popupControl.close()
  }

  showPicker.value = false
}

/**
 * 取消选择
 */
const onCancel = () => {
  // 调用父组件提供的 close 函数
  if (popupControl && popupControl.close) {
    popupControl.close()
  }

  showPicker.value = false
}
</script>

<style lang="less">
/* 组件样式 */
</style>