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
相关产品推荐
相关产品推荐

