javascript
复制代码
<template>
<el-input ref="inputRef" :disabled="disabled" clearable v-model="displayValue" v-bind="$attrs" @input="handleInput" @focus="handleFocus" @blur="handleBlur" @change="handleChange" @keydown="handleKeyDown" @compositionstart="handleCompositionStart" @compositionend="handleCompositionEnd"></el-input>
</template>
<script>
export default {
name: 'AmountInput',
props: {
value: {
type: [Number, String],
default: null,
},
disabled: {
type: Boolean,
default: false,
},
// 新增精度控制属性
precision: {
type: Number,
default: 2,
},
// 新增:是否启用大数模式(字符串处理)
bigNumber: {
type: Boolean,
default: true,
},
},
data() {
return {
displayValue: '',
internalValue: null,
isFocused: false,
lastValidValue: null,
isComposing: false, // 标记是否在中文输入法组合输入中
};
},
watch: {
value: {
immediate: true,
handler(newVal) {
const parsed = this.parseInputValue(newVal);
if (parsed !== this.internalValue) {
this.internalValue = parsed;
this.formatDisplayValue();
}
},
},
},
mounted() {
this.internalValue = this.parseInputValue(this.value);
this.formatDisplayValue();
},
methods: {
// 确保转换为字符串处理
safeToString(value) {
if (value === null || value === undefined) return '';
return String(value);
},
// 处理中文输入法开始
handleCompositionStart() {
this.isComposing = true;
},
// 处理中文输入法结束
handleCompositionEnd(e) {
this.isComposing = false;
this.handleInput(e.target.value);
},
// 处理大数字符串
processBigNumber(numStr) {
console.log(numStr, 'numStr');
if (!numStr) return '';
// 移除所有非数字字符(保留负号和小数点)
let cleaned = numStr.replace(/[^\d.-]/g, '');
// 分离符号、整数和小数部分
const isNegative = cleaned.startsWith('-');
if (isNegative) cleaned = cleaned.substring(1);
const parts = cleaned.split('.');
let integerPart = parts[0].replace(/^0+/, '') || '0';
let decimalPart = parts.length > 1 ? parts[1] : '';
// 截断小数部分
if (decimalPart.length > this.precision) {
decimalPart = decimalPart.substring(0, this.precision);
}
// 添加千分位
integerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// 重新组合
return (isNegative ? '-' : '') + integerPart + (decimalPart ? `.${decimalPart}` : '');
},
formatDisplayValue() {
if (this.isFocused) {
this.displayValue = this.internalValue !== null ? this.safeToString(this.internalValue) : '';
} else {
this.displayValue = this.formatNumber(this.internalValue);
}
},
parseInputValue(value) {
if (value === '' || value === null || value === undefined) return null;
const numStr = this.safeToString(value)
.replace(/,/g, '')
.replace(/[^\d.-]/g, '');
if (numStr === '-' || numStr === '.') return null;
// 处理大数字符串
if (numStr.length > 15) {
return numStr; // 作为字符串返回,避免精度丢失
}
const num = Number(numStr);
return isNaN(num) ? null : num;
},
// 修复科学计数法转换
convertFromScientificNotation(num) {
if (num === null || num === undefined) return null;
// 如果是字符串形式的科学计数法
if (typeof num === 'string' && num.includes('e')) {
try {
return Number(num).toString();
} catch {
return num;
}
}
// 如果是数值型的科学计数法
if (typeof num === 'number' && num.toString().includes('e')) {
return num.toLocaleString('fullwide', { useGrouping: false });
}
return num.toString();
},
// 增强的数字格式化方法
formatNumber(value) {
if (value === null || value === undefined) return '';
const numStr = this.safeToString(value);
const isNegative = numStr.startsWith('-');
const cleanStr = isNegative ? numStr.substring(1) : numStr;
const parts = cleanStr.split('.');
let integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
let decimalPart = parts.length > 1 ? parts[1].substring(0, this.precision) : '';
return (isNegative ? '-' : '') + integerPart + (decimalPart ? `.${decimalPart}` : '');
},
validateInput(value) {
if (value === '' || value === null) return true;
return new RegExp(`^-?\\d*\\.?\\d{0,${this.precision}}$`).test(value);
},
handleInput(value) {
if (this.isComposing) return; // 中文输入法组合期间不处理
const strValue = this.safeToString(value);
const filtered = strValue.replace(/[^\d.-]/g, '');
// 处理多个小数点
const decimalParts = filtered.split('.');
let cleanedValue = decimalParts[0];
if (decimalParts.length > 1) {
cleanedValue += '.' + decimalParts.slice(1).join('').substring(0, this.precision);
}
// 处理负号
if (cleanedValue.includes('-')) {
cleanedValue = '-' + cleanedValue.replace(/-/g, '');
}
if (cleanedValue !== this.displayValue) {
this.displayValue = cleanedValue;
}
const parsed = this.parseInputValue(cleanedValue);
this.internalValue = parsed;
this.lastValidValue = cleanedValue;
this.$emit('input', parsed);
},
handleFocus() {
this.isFocused = true;
this.formatDisplayValue();
this.$emit('focus');
},
handleBlur() {
this.isFocused = false;
const parsed = this.parseInputValue(this.displayValue);
this.internalValue = parsed;
this.formatDisplayValue();
this.$emit('blur');
this.$emit('change', parsed);
},
handleKeyDown(e) {
const allowedKeys = [
8,
9,
37,
39,
46,
35,
36, // 特殊键
...(e.ctrlKey ? [65, 67, 86, 88] : []), // Ctrl组合键
];
if (/\d/.test(e.key) || allowedKeys.includes(e.keyCode) || (e.key === '.' && !this.displayValue.includes('.')) || (e.key === '-' && !this.displayValue.includes('-') && (this.displayValue === '' || e.target.selectionStart === 0))) return;
e.preventDefault();
},
},
};
</script>