AgePicker.vue 3.98 KB
<template>
  <div>
    <!-- 标签 -->
    <div v-if="label" class="text-sm text-gray-600 mb-2">{{ label }}</div>

    <!-- 触发区域 -->
    <div
      class="flex justify-between items-center border border-gray-200 rounded-lg p-3"
      :class="{ 'bg-gray-50': showPicker }"
      @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"
    >
      <nut-picker
        :columns="ageColumns"
        :default-value="defaultValue"
        @confirm="onConfirm"
        @cancel="showPicker = false"
      />
    </nut-popup>
  </div>
</template>

<script setup>
/**
 * 年龄选择器组件
 *
 * @description 使用 NutUI Popup + Picker 实现年龄选择
 *              - 显示格式:3位数字(如 018 表示 18 岁)
 *              - 提交格式:数字(如 18)
 *              - 年龄范围:0-120 岁
 * @author Claude Code
 * @example
 * <AgePicker
 *   v-model="age"
 *   label="年龄"
 *   placeholder="请选择年龄"
 * />
 */
import { ref, computed } from 'vue'
import IconFont from '@/components/IconFont.vue'

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

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

  /**
   * 绑定的值(数字)
   * @type {number}
   */
  modelValue: {
    type: Number,
    default: null
  }
})

/**
 * 组件事件
 */
const emit = defineEmits([
  /**
   * 更新值事件
   * @event update:modelValue
   * @param {number} value - 选中的年龄(数字)
   */
  'update:modelValue'
])

/**
 * 控制 Picker 显示
 * @type {Ref<boolean>}
 */
const showPicker = ref(false)

/**
 * 打开选择器
 */
const openPicker = () => {
  showPicker.value = true
}

/**
 * 年龄选项(3位数字格式)
 * @description 生成 000-120 的年龄选项数组
 * @returns {Array<Array<{text: string, value: number}>>} Picker 列格式
 *
 * @example
 * // 返回值示例
 * [
 *   [
 *     { text: '000', value: 0 },
 *     { text: '001', value: 1 },
 *     ...
 *     { text: '018', value: 18 },
 *     ...
 *     { text: '120', value: 120 }
 *   ]
 * ]
 */
const ageColumns = computed(() => {
  const ages = []
  for (let i = 0; i <= 120; i++) {
    // 0, 1, 2 -> '000', '001', '002'
    const ageStr = i.toString().padStart(3, '0')
    ages.push({ text: ageStr, value: i })
  }
  return [ages]
})

/**
 * 默认选中的值(3位数字格式)
 * @description 如果没有值,默认显示 018(18岁)
 * @returns {Array<string>} Picker 默认值格式
 *
 * @example
 * // modelValue = 18
 * defaultValue() // 返回: ['018']
 *
 * // modelValue = null
 * defaultValue() // 返回: ['018']
 */
const defaultValue = computed(() => {
  const age = props.modelValue || 18
  return [age.toString().padStart(3, '0')]
})

/**
 * 显示的值(数字格式)
 * @description 将数字转换为字符串显示
 * @returns {string} 显示文本
 *
 * @example
 * // modelValue = 18
 * displayValue() // 返回: '18'
 *
 * // modelValue = null
 * displayValue() // 返回: ''
 */
const displayValue = computed(() => {
  return props.modelValue ? props.modelValue.toString() : ''
})

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

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