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

Vuetify使用v-menu绑定自定义数字键盘实现多文本输入框编辑问题

实现方案

第一步:修改自定义数字键盘(Calculator)组件

补充组件初始化赋值逻辑、保存/取消的事件触发逻辑,同时优化重复的数字点击代码:

<template>
  <v-card class="widget" @contextmenu.prevent="" color="grey lighten-2" max-width="260" >
    <v-card-text>
      <v-card class="mb-2" elevation="1" min-height="64px" max-height="64px" tile flat color="lighten-grey" >
        <v-card-text>
          <v-text-field dense flat reverse v-model="value" :suffix="suffix" ></v-text-field>
        </v-card-text>
      </v-card>
      <v-row no-gutters class="pb-1">
        <v-col align="center" justify="center">
          <v-btn color="grey lighten-5" @click="inputNum(1)" tile elevation="1" height="55px" >1</v-btn>
        </v-col>
        <v-col align="center" justify="center">
          <v-btn color="grey lighten-5" @click="inputNum(2)" tile elevation="1" height="55px" >2</v-btn>
        </v-col>
        <v-col align="center" justify="center">
          <v-btn color="grey lighten-5" @click="inputNum(3)" tile elevation="1" height="55px" >3</v-btn>
        </v-col>
      </v-row>
      <v-row no-gutters class="pb-1">
        <v-col align="center" justify="center">
          <v-btn color="grey lighten-5" @click="inputNum(0)" tile elevation="1" height="55px" >0</v-btn >
        </v-col>
        <v-col align="center" justify="center">
          <v-btn color="grey lighten-5" @click="clickDecimal" tile elevation="1" height="55px" >.</v-btn >
        </v-col>
        <v-col align="center" justify="center">
          <v-btn color="grey lighten-5" @click="clickClear" tile elevation="1" height="55px" >C</v-btn >
        </v-col>
      </v-row>
      <v-row no-gutters class="pt-2">
        <v-col align="right">
          <v-btn @click="cancel" tile small>取消</v-btn>
          <v-btn class="ml-2 mr-0" @click="onSave" tile small>保存</v-btn>
        </v-col>
      </v-row>
    </v-card-text>
  </v-card>
</template>

<script>
export default {
  props: ['data', 'suffix'],
  data() {
    return { 
      value: '0', 
    };
  },
  watch: {
    // 每次父组件传入的绑定值变化时,同步更新键盘内的输入值
    data: {
      immediate: true,
      handler(val) {
        this.value = val?.toString() || '0'
      }
    }
  },
  methods: {
    // 统一处理数字输入,减少重复代码
    inputNum(num) {
      if (this.value.toString().length > 9) return;
      if (this.value === '0') { this.value = ''; }
      this.value = this.value + num.toString();
    },
    clickClear() {
      this.value = '0';
    },
    clickDecimal() {
      if (this.value.includes('.')) return;
      this.value = this.value + '.';
    },
    onSave() {
      // 同步修改父组件绑定的值,兼容.sync修饰符
      this.$emit('update:data', this.value)
      // 触发保存事件,通知父组件关闭菜单
      this.$emit('save')
    },
    cancel() {
      // 触发取消事件,通知父组件关闭菜单
      this.$emit('cancel')
    },
  },
};
</script>

第二步:调整父组件调用逻辑

给v-menu增加显示隐藏的绑定,监听键盘组件的保存/取消事件控制菜单关闭,多输入框直接复制对应代码绑定不同变量即可:

<template>
  <!-- 第一个数字输入框 -->
  <v-menu 
    bottom 
    :close-on-content-click="false"
    v-model="menu1Open"
  >
    <template v-slot:activator="{ on, attrs }">
      <v-text-field
        v-bind="attrs"
        v-on="on"
        v-model="result1"
        readonly
        label="商品价格"
      ></v-text-field>
    </template>
    <Calculator 
      :data.sync="result1" 
      @save="menu1Open = false"
      @cancel="menu1Open = false"
      suffix="元"
    />
  </v-menu>

  <!-- 第二个数字输入框 -->
  <v-menu 
    bottom 
    :close-on-content-click="false"
    v-model="menu2Open"
    class="mt-4"
  >
    <template v-slot:activator="{ on, attrs }">
      <v-text-field
        v-bind="attrs"
        v-on="on"
        v-model="result2"
        readonly
        label="折扣比例"
      ></v-text-field>
    </template>
    <Calculator 
      :data.sync="result2" 
      @save="menu2Open = false"
      @cancel="menu2Open = false"
      suffix="%"
    />
  </v-menu>
</template>

<script>
import Calculator from './Calculator.vue'
export default {
  components: { Calculator },
  data() {
    return {
      menu1Open: false,
      result1: '0',
      menu2Open: false,
      result2: '0'
    }
  }
}
</script>

第三步:更高复用性封装(可选)

如果需要大量使用该输入组件,可以把v-menu+文本框的逻辑再封装成独立组件NumericInput.vue,直接支持v-model调用:

<!-- NumericInput.vue -->
<template>
  <v-menu 
    bottom 
    :close-on-content-click="false"
    v-model="menuOpen"
  >
    <template v-slot:activator="{ on, attrs }">
      <v-text-field
        v-bind="attrs"
        v-on="on"
        v-model="localValue"
        readonly
        :label="label"
        :suffix="suffix"
      ></v-text-field>
    </template>
    <Calculator 
      :data.sync="localValue" 
      @save="menuOpen = false"
      @cancel="menuOpen = false"
      :suffix="suffix"
    />
  </v-menu>
</template>

<script>
import Calculator from './Calculator.vue'
export default {
  components: { Calculator },
  props: ['value', 'label', 'suffix'],
  data() {
    return {
      menuOpen: false,
      localValue: this.value
    }
  },
  watch: {
    value(val) {
      this.localValue = val
    },
    localValue(val) {
      this.$emit('input', val)
    }
  }
}
</script>

封装后调用非常简洁,不需要重复写菜单逻辑:

<template>
  <NumericInput v-model="form.price" label="商品价格" suffix="元" />
  <NumericInput v-model="form.discount" label="折扣" suffix="%" />
  <NumericInput v-model="form.stock" label="库存" suffix="件" />
</template>

该实现完全适配大屏触摸场景,不会触发系统软键盘,可直接多输入框复用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 15:06:04