AntDesignVue 数值范围组件 NumberRange.vue

发布者:站点默认
2025/04/10 浏览数(31) 分类:VueComponentLibrary AntDesignVue 数值范围组件 NumberRange.vue已关闭评论

介绍

validatable: 是否使用组件内置的校验
autoCorrect: min > max 时自动换位、autoCorrect:true + allowHalf:false 时可能会将 null 变为 0
allowHalf: 是否允许「一个数字、一个 null」、autoCorrect:true + allowHalf:false 时将 null 变为 0

用法

<NumberRange v-model:value="form.valueRange" />
<NumberRange v-model:value="form.valueRange" :allowHalf="false" />
<NumberRange v-model:value="form.valueRange" :autoCorrect="true" />
<NumberRange v-model:value="form.valueRange" :autoCorrect="true" :allowHalf="false" />

源码

<!--
数值区间组件

用法:
<NumberRange v-model:value="form.valueRange" />
<NumberRange v-model:value="form.valueRange" :allowHalf="false" />
<NumberRange v-model:value="form.valueRange" :autoCorrect="true" />
<NumberRange v-model:value="form.valueRange" :autoCorrect="true" :allowHalf="false" />

返回值:
1. 当2个值其中一个不是null时返回数组,例: [null,6]、[9,null]
1. 当2个值都为 null 时返回 null
1. 当2个值都不是 null 时返回数组例: [9,6]、[6,9]
1. 使用了 a-input-number 所以不会出现  ['',null]、['','']
-->
<template>
  <div class="number-range">
    <a-input-number v-model:value="minValue" placeholder="请填写" :allowClear="clearable" :status="errorStatus" @blur="handleBlur()" @focus="handleFocus()" @change="handleChange()" />
    <div class="number-range-separator">~</div>
    <a-input-number v-model:value="maxValue" placeholder="请填写" :allowClear="clearable" :status="errorStatus" @blur="handleBlur()" @focus="handleFocus()" @change="handleChange()" />
  </div>
  <div class="ant-form-item-explain-error" v-if="errorMessage">
    {{ errorMessage }}
  </div>
</template>

<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue'

interface Props {
  value?: Array<number | null> | null
  clearable?: boolean // 不起作用:a-input-number 没有这个功能
  validatable?: boolean // 是否使用组件自带的数据验证
  autoCorrect?: boolean // 当两个值写反时自动换位置,此功能会影响用户手动输入建议不要开启;当不允许其中一个值为null时自动补0
  allowHalf?: boolean // 是否允许其中一个值为 null,false 时当「自动修正」则 null 返回为 0
}

const props = withDefaults(defineProps<Props>(), {
  value: () => [null, null],
  clearable: true,
  validatable: true,
  autoCorrect: false,
  allowHalf: true,
})

const emit = defineEmits(['update:value', 'change', 'validate'])

const formState = reactive({
  min: null as number | null,
  max: null as number | null,
})

const errorMessage = ref('')
const errorStatus = computed(() => (errorMessage.value ? 'error' : ''))

const validate = () => {
  errorMessage.value = ''
  if (!props.validatable) {
    return true
  }
  if (formState.min != null && formState.max != null && formState.min > formState.max) {
    if (props.autoCorrect) {
      ;[formState.min, formState.max] = [formState.max, formState.min]
      updateValue()
    } else {
      errorMessage.value = '左侧值不能大于右侧值'
      return false
    }
  }
  // 当一个值为空另一个值不为空时
  if ((formState.min === null && formState.max !== null) || (formState.min !== null && formState.max === null)) {
    if (!props.allowHalf) {
      if (props.autoCorrect) {
        formState.min = formState.min ?? 0
        formState.max = formState.max ?? 0
      } else {
        errorMessage.value = `${!formState.min ? '左侧' : '右侧'}值不可以为空`
        return false
      }
    }
  }
  return true
}

const minValue = computed({
  get: () => formState.min,
  set: (val) => {
    formState.min = val
    updateValue()
  },
})

const maxValue = computed({
  get: () => formState.max,
  set: (val) => {
    formState.max = val
    updateValue()
  },
})

watch(
  () => props.value,
  (newVal) => {
    if (newVal) {
      formState.min = newVal[0]
      formState.max = newVal[1]
    }
  },
  { immediate: true },
)

const updateValue = () => {
  emit('update:value', formState.min == null && formState.max == null ? null : [formState.min, formState.max])
  emit('change', [formState.min, formState.max])
  validate()
  emit('validate', !errorMessage.value)
}

const handleBlur = () => {
  validate()
}

const handleFocus = () => {
  validate()
}

const handleChange = () => {
  validate()
}

defineExpose({
  validate,
})
</script>

<style lang="less" scoped>
.number-range {
  width: 100%;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  color: rgba(0, 0, 0, 0.88);
  font-size: 14px;
  line-height: 1;
  list-style: none;
  position: relative;
  display: inline-flex;
  align-items: center;
  background: #ffffff;
  transition:
    border 0.2s,
    box-shadow 0.2s;
  .number-range-separator {
    color: rgba(0, 0, 0, 0.25);
    width: 1.5em;
    text-align: center;
  }
  .ant-input-number {
    flex: 1;
    &.ant-input-number-focused {
      box-shadow: none;
    }
    :deep(.ant-input-number-input) {
      text-align: center;
    }
  }
}
.ant-form-item-explain-error {
  height: 0;
}
</style>
点击返回顶部
  1. 留言
  2. 联系方式