Files
hht-ynhl-one-uni/components/NumberInput.vue

123 lines
2.8 KiB
Vue
Raw Normal View History

2025-05-30 17:36:59 +08:00
<template>
<input
type="text"
:value="displayValue"
@input="handleInput"
@blur="handleBlur"
:placeholder="placeholder"
:class="inputClass"
:style="inputStyle"
/>
</template>
<script>
export default {
name: "NumberInput",
props: {
value: {
type: [Number, String],
default: ''
},
min: {
type: Number,
default: 0.001
},
max: {
type: Number,
default: 100000
},
placeholder: {
type: String,
default: '请输入大于0的数字'
},
inputClass: {
type: String,
default: ''
},
inputStyle: {
type: String,
default: ''
}
},
data() {
return {
displayValue: this.value.toString()
};
},
watch: {
value(newVal) {
// 当外部value变化时更新显示值
this.displayValue = newVal.toString();
}
},
methods: {
handleInput(e) {
const rawValue = e.detail.value;
let filtered = rawValue
// 移除非数字和小数点的字符
.replace(/[^\d.]/g, '')
// 只保留第一个小数点
.replace(/(\.+)/g, '.')
.replace(/(\..*)\./g, '$1')
// 避免以0开头0后面没有小数点的情况
.replace(/^0+(\d)/, '$1');
// 处理开头是小数点的情况添加0前缀
if (filtered.startsWith('.')) {
filtered = '0' + filtered;
}
this.displayValue = filtered;
this.$emit('input', filtered);
},
handleBlur() {
let finalValue = this.displayValue;
// 空值处理
if (!finalValue) {
finalValue = this.min.toString();
}
// 处理以小数点结尾的情况
if (finalValue.endsWith('.')) {
finalValue = finalValue.slice(0, -1);
}
// 转换为数字
let numValue = parseFloat(finalValue);
// 验证数字有效性
if (isNaN(numValue)) {
numValue = this.min;
}
// 边界校验
if (numValue <= 0) {
numValue = this.min;
} else if (numValue > this.max) {
numValue = this.max;
} else if (numValue < this.min) {
numValue = this.min;
}
// 小数位处理最多3位
const numStr = numValue.toString();
const decimalIndex = numStr.indexOf('.');
if (decimalIndex !== -1) {
const decimalPart = numStr.slice(decimalIndex + 1);
if (decimalPart.length > 3) {
// 直接截断而非四舍五入
numValue = parseFloat(numStr.substring(0, decimalIndex + 4));
}
}
// 更新显示值并通知父组件
this.displayValue = numValue.toString();
this.$emit('input', numValue);
this.$emit('change', numValue);
}
}
}
</script>