123 lines
2.8 KiB
Vue
123 lines
2.8 KiB
Vue
|
|
<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>
|