
介绍
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>