华西海圻ELN前端工程
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

312 lines
9.3 KiB

<template>
<el-input v-bind="$attrs" v-model="internalValue" @input="handleInput" @blur="handleBlur" :placeholder="placeholder"
:disabled="disabled" type="text">
<template slot="prepend" v-if="prepend">
{{ prepend }}
</template>
</el-input>
</template>
<script>
export default {
name: 'DecimalInput',
props: {
value: {
type: [Number, String],
default: ''
},
decimalDigits: {
type: Number,
default: 6
},
placeholder: {
type: String,
default: '请输入'
},
disabled: {
type: Boolean,
default: false
},
prepend: {
type: String,
default: ''
},
},
data() {
return {
internalValue: this.value !== null && this.value !== undefined ? String(this.value) : '',
oldValue: null,
oldPattern: null,
patternRules: [
{ name: 'NA', pattern: /^NA$/i, inputPattern: /^N?A?$/i },
{ name: 'FRACTION', pattern: /^\d+(\/\d+)*$/, inputPattern: /^(\d+\/?)*$/ }
]
};
},
watch: {
value: {
handler(newVal) {
if (newVal === '' || newVal == null) {
this.internalValue = '';
this.oldValue = null;
this.oldPattern = null;
} else {
const strVal = String(newVal);
this.internalValue = strVal;
this.updateOldValue(strVal);
}
},
immediate: true
}
},
methods: {
updateOldValue(val) {
for (const rule of this.patternRules) {
if (rule.pattern.test(val)) {
this.oldValue = val;
this.oldPattern = rule;
return true;
}
}
return false;
},
getMatchingRule(val) {
for (const rule of this.patternRules) {
if (rule.inputPattern.test(val)) {
return rule;
}
}
return null;
},
handleInput(val) {
if (val === '') {
this.internalValue = '';
this.$emit('input', '');
return;
}
const upperVal = val.toUpperCase();
let cleaned = val;
let matchedRule = null;
if (this.oldPattern) {
// 检查是否是旧模式的延续(以oldValue开头)
if (val.startsWith(this.oldValue)) {
// 是旧模式的延续,检查是否符合inputPattern
if (this.oldPattern.inputPattern.test(val)) {
matchedRule = this.oldPattern;
cleaned = val;
} else {
// 不符合,但如果是FRACTION类型,尝试特殊处理
if (this.oldPattern.name === 'FRACTION') {
cleaned = val.replace(/[^\d/]/g, '');
const parts = cleaned.split('/');
const validParts = [];
for (let i = 0; i < parts.length; i++) {
if (parts[i] !== '') {
validParts.push(parts[i]);
}
}
cleaned = validParts.join('/');
matchedRule = this.oldPattern;
} else {
cleaned = this.oldValue || '';
this.internalValue = cleaned;
return;
}
}
} else if (this.oldPattern.inputPattern.test(val)) {
// 不是延续,但符合inputPattern(可能是清空后重新输入)
matchedRule = this.oldPattern;
cleaned = val;
} else {
// 尝试匹配新规则
matchedRule = this.getMatchingRule(val);
if (!matchedRule) {
cleaned = this.oldValue || '';
this.internalValue = cleaned;
return;
}
}
} else {
matchedRule = this.getMatchingRule(val);
if (matchedRule) {
if (matchedRule.name === 'FRACTION') {
cleaned = val.replace(/[^\d/]/g, '');
const parts = cleaned.split('/');
const validParts = [];
for (let i = 0; i < parts.length; i++) {
if (parts[i] !== '') {
validParts.push(parts[i]);
}
}
cleaned = validParts.join('/');
} else {
cleaned = upperVal;
}
} else {
cleaned = val
.replace(/[^\d.-]/g, '')
.replace(/^(-)\1+/, '$1');
const firstDotIndex = cleaned.indexOf('.');
if (firstDotIndex !== -1) {
const before = cleaned.slice(0, firstDotIndex);
const after = cleaned.slice(firstDotIndex + 1).replace(/\./g, '');
cleaned = before + '.' + after;
}
if (this.decimalDigits > 0 && cleaned.includes('.')) {
const [intPart, decPart = ''] = cleaned.split('.');
cleaned = intPart + '.' + decPart.slice(0, this.decimalDigits);
} else if (this.decimalDigits === 0) {
cleaned = cleaned.split('.')[0];
}
if (cleaned === '.') cleaned = '0.';
else if (cleaned === '-.') cleaned = '-0.';
else if (cleaned.startsWith('.')) cleaned = '0' + cleaned;
else if (cleaned.startsWith('-.')) cleaned = '-0.' + cleaned.slice(2);
if (cleaned.includes('.')) {
const [int, dec] = cleaned.split('.');
let newInt = int;
if (/^-?0+\d/.test(int)) {
newInt = int.replace(/^-?0+(\d)/, '$1');
} else if (int === '' || int === '-') {
newInt = int + '0';
} else if (int === '00' || /^-00+$/.test(int)) {
newInt = int.startsWith('-') ? '-0' : '0';
}
cleaned = newInt + '.' + dec;
} else {
if (/^-?0+\d/.test(cleaned)) {
cleaned = cleaned.replace(/^-?0+(\d)/, '$1');
} else if (cleaned === '00' || /^-00+$/.test(cleaned)) {
cleaned = cleaned.startsWith('-') ? '-0' : '0';
}
}
}
}
this.internalValue = cleaned;
if (cleaned === '' || cleaned === '-') {
this.$emit('input', cleaned === '-' ? '-' : '');
} else if (matchedRule && matchedRule.name !== 'FRACTION') {
this.$emit('input', cleaned);
} else if (cleaned.includes('/')) {
this.$emit('input', cleaned);
} else {
const num = parseFloat(cleaned);
this.$emit('input', isNaN(num) ? '' : num);
}
},
handleDecimalDigits(val) {
const actVal = val || this.internalValue;
let finalValue = actVal.trim();
if (finalValue === '' || finalValue === '-') {
return '';
}
const num = parseFloat(finalValue);
if (isNaN(num)) {
return '';
}
let formatted = String(num);
if (!finalValue.includes('.')) {
formatted = String(Math.floor(num));
} else {
const decPart = finalValue.split('.')[1];
const actualDecimalDigits = decPart.length;
const displayDecimalDigits = Math.min(actualDecimalDigits, this.decimalDigits);
formatted = num.toFixed(displayDecimalDigits).replace(/\.?0*$/, '');
}
return formatted;
},
handleBlur() {
const val = this.internalValue.trim();
if (val === '') {
this.oldValue = null;
this.oldPattern = null;
this.$emit('input', '');
this.$emit('blur', '');
return;
}
const upperVal = val.toUpperCase();
for (const rule of this.patternRules) {
if (rule.pattern.test(upperVal)) {
this.oldValue = upperVal;
this.oldPattern = rule;
this.internalValue = upperVal;
this.$emit('input', upperVal);
this.$emit('blur', upperVal);
return;
}
}
if (val.includes('/')) {
const parts = val.split('/');
const validParts = parts.filter(part => part !== '');
if (validParts.length === 0) {
if (this.oldValue) {
this.internalValue = this.oldValue;
this.$emit('input', this.oldValue);
this.$emit('blur', this.oldValue);
} else {
this.internalValue = '';
this.$emit('input', '');
this.$emit('blur', '');
}
return;
}
if (validParts.length === 1) {
const result = validParts[0];
this.oldValue = result;
this.oldPattern = this.patternRules.find(r => r.name === 'FRACTION');
this.internalValue = result;
this.$emit('input', result);
this.$emit('blur', result);
return;
}
const formattedValue = validParts.join('/');
this.oldValue = formattedValue;
this.oldPattern = this.patternRules.find(r => r.name === 'FRACTION');
this.internalValue = formattedValue;
this.$emit('input', formattedValue);
this.$emit('blur', formattedValue);
return;
}
if (this.oldValue && this.oldPattern) {
const partialMatch = this.oldPattern.inputPattern && this.oldPattern.inputPattern.test(val);
if (!partialMatch) {
this.internalValue = this.oldValue;
this.$emit('input', this.oldValue);
this.$emit('blur', this.oldValue);
return;
}
}
let formatted = this.handleDecimalDigits(val);
this.internalValue = formatted;
this.$emit('input', parseFloat(formatted));
this.$emit('blur', parseFloat(formatted));
}
}
};
</script>