From beb9e6e8c6c5720894642e4286e3cf6d34ff7ebb Mon Sep 17 00:00:00 2001 From: luojie <125330818@qq.com> Date: Fri, 6 Mar 2026 20:39:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:[=E6=A8=A1=E6=9D=BF=E7=AE=A1=E7=90=86][?= =?UTF-8?q?=E6=9A=82=E5=AD=98]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Template/DecimalInput.vue | 285 ++++++++++++++++----- .../Template/mixins/formPackageMixins.js | 2 - 2 files changed, 218 insertions(+), 69 deletions(-) diff --git a/src/components/Template/DecimalInput.vue b/src/components/Template/DecimalInput.vue index cc8e23a..e5adbd9 100644 --- a/src/components/Template/DecimalInput.vue +++ b/src/components/Template/DecimalInput.vue @@ -35,20 +35,52 @@ export default { }, data() { return { - internalValue: this.value !== null && this.value !== undefined ? String(this.value) : '' + 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(newVal) { - // 外部值变化时同步到内部(但不做格式化,避免干扰用户输入) - if (newVal === '' || newVal == null) { - this.internalValue = ''; - } else { - this.internalValue = this.handleDecimalDigits(String(newVal)); - } + 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 = ''; @@ -56,91 +88,142 @@ export default { return; } - // 1. 只保留数字、小数点、开头的负号 - let cleaned = val - .replace(/[^\d.-]/g, '') - .replace(/^(-)\1+/, '$1'); // 合并多个负号 - - // 2. 只保留第一个小数点 - const firstDotIndex = cleaned.indexOf('.'); - if (firstDotIndex !== -1) { - const before = cleaned.slice(0, firstDotIndex); - const after = cleaned.slice(firstDotIndex + 1).replace(/\./g, ''); - cleaned = before + '.' + after; - } - - // 3. 限制小数位数(仅当允许小数时) - 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]; // 移除小数部分 - } - - // 4. 处理以 . 或 -. 开头 - 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); - - // 5. 【关键】安全去除前导零,但保留单个 0 - if (cleaned.includes('.')) { - // 有小数点:处理整数部分 - const [int, dec] = cleaned.split('.'); - // 整数部分:-0012 → -12,00 → 0,0 → 0,-0 → 0(或保留 -0) - 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'; + 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; + } } - 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'; + 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'; + } + } } - // 注意:不要把单独的 '0' 变成 '' } + this.internalValue = cleaned; - // emit 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 === '-') { - this.internalValue = ''; - this.$emit('input', ''); - return; + return ''; } const num = parseFloat(finalValue); if (isNaN(num)) { - this.internalValue = ''; - this.$emit('input', ''); - return; + 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); @@ -150,12 +233,80 @@ export default { }, handleBlur() { - let formatted = this.handleDecimalDigits(this.internalValue); + 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; - // emit 数字类型(也可 emit 字符串,根据需求) this.$emit('input', parseFloat(formatted)); this.$emit('blur', parseFloat(formatted)); } } }; - \ No newline at end of file + diff --git a/src/components/Template/mixins/formPackageMixins.js b/src/components/Template/mixins/formPackageMixins.js index 2ed467e..77f44da 100644 --- a/src/components/Template/mixins/formPackageMixins.js +++ b/src/components/Template/mixins/formPackageMixins.js @@ -67,7 +67,6 @@ export default { }, getRegentItem(item, fieldCode = "type") { const type = item[fieldCode]; - console.log(item, "type") return { label: "", type, @@ -391,7 +390,6 @@ export default { const {otherCode} = o; const otherValue = formFields[otherCode] || ""; const isShow = this.isShowOtherByRadioAndOther(radioValue,o) - console.log(o,radioValue,isShow,otherValue,"radioValue") if(isShow&&!otherValue){ errors.push({ field: key,