You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vuetify v-text-field金额格式化失焦后显示旧值问题求助

失焦后金额输入组件显示旧值的原因及修复方案

问题原因

  1. 双向绑定逻辑混乱:你同时混用了Vue 2的model选项(绑定value和input事件)与Vue 3风格的@update:model-value事件,两种v-model语法冲突,导致失焦时重复触发值更新操作,新值被旧值覆盖。
  2. betrag与Imask实例同步错误:父组件更新betrag时,watch直接设置masked.unmaskedValue,但未确保输入框显示的格式化值同步更新;同时失焦时触发的emit会再次触发watch,将旧值重新塞回Imask,造成显示回退。
  3. 输入框绑定值未实时响应:v-text-field的model-value绑定的是masked.value,但Imask内部值变化后,输入框未实时更新;当失焦父组件更新betrag时,watch触发导致显示回到旧值。

修复方案

1. 统一双向绑定逻辑

移除Vue 3风格的@update:model-value事件,仅使用Vue 2的model选项,将prop直接绑定到betrag,确保绑定逻辑一致。

2. 优化betrag与Imask的同步逻辑

  • 监听betrag时先判断Imask实例是否初始化,避免报错;
  • 利用Imask自带的onAccept事件自动同步未掩码值到父组件,替代手动在input和blur事件中重复emit。

3. 确保输入框实时显示最新值

添加计算属性处理显示值,初始化前展示格式化后的betrag,初始化后展示Imask的格式化值,保证任何场景下显示正确。

修改后的完整代码

<template>
  <v-container class="pa-0" v-if="$attrs.editable">
    <v-text-field
      ref="field"
      :model-value="displayValue"
      @blur="handleBlur"
      v-bind="$attrs"
      density="compact"
      variant="outlined"
    ></v-text-field>
  </v-container>
  <v-container class="pa-0" v-else>
    {{ formatBetrag(betrag) }}
  </v-container>
</template>

<script lang="ts">
import Imask from 'imask'

const INPUT_EVENT = 'input'

export default {
  name: 'ImaskField',
  components: { BetragsFeld },

  model: {
    prop: 'betrag',
    event: INPUT_EVENT
  },

  props: {
    betrag: {
      type: [String, Number],
      required: false,
      default: 0
    },
  },

  computed: {
    displayValue() {
      // 初始化前返回格式化后的金额
      if (!this.masked) {
        return this.formatBetrag(this.betrag)
      }
      return this.masked.value
    }
  },

  data: () => ({
    element: null,
    masked: null,
  }),

  watch: {
    betrag(val) {
      if (this.masked) {
        // 仅当值不同时更新,避免循环触发
        if (String(this.masked.unmaskedValue) !== String(val)) {
          this.masked.unmaskedValue = String(val)
        }
      }
    }
  },

  methods: {
    formatBetrag(value) {
      if (value === undefined || value === null) return ''
      return Number(value).toLocaleString('de-DE', {
        minimumFractionDigits: 2,
        maximumFractionDigits: 2
      })
    },

    init() {
      this.element = this.$refs.field.$el.querySelector('input')
      this.masked = Imask(this.element, {
        mask: Number,
        thousandsSeparator: '.',
        scale: 2,
        padFractionalZeros: true,
        normalizeZeros: true,
        radix: ',',
        // 用Imask自带事件同步值到父组件
        onAccept: () => {
          const unmaskedValue = Number(this.masked.unmaskedValue)
          this.$emit(INPUT_EVENT, unmaskedValue)
        }
      })
      // 初始化时设置初始值
      this.masked.unmaskedValue = String(this.betrag)
    },

    handleBlur() {
      // 失焦时确保值规范化并强制更新显示
      if (this.masked) {
        this.masked.updateValue()
        this.$forceUpdate()
      }
    }
  },

  mounted() {
    if (this.$attrs.editable) {
      // 延迟初始化确保DOM渲染完成
      this.$nextTick(() => this.init())
    }
  }
}
</script>

内容的提问来源于stack exchange,提问作者Jens

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 18:23:12