Я пытаюсь добавить форматированное поле в свое приложение vue, используя маску. До сих пор все работает нормально, если мое значение не содержит цифр фракции (например, 26767).
Но если я получу значение с фракцией типа 26717.01, я получу результат 2.671.701 (я ожидаю 26.717,01)
Может ли кто-нибудь указать мне правильное направление?
<script setup lang = "ts">
import { vMaska } from "maska/vue";
const emit = defineEmits([ 'update:modelValue']);
let props = defineProps({
amount: {
type: Number
},
})
const options = {
number:{
fraction: 2,
locale: 'de-DE'
},
};
</script>
<script lang = "ts">
import {defineComponent} from "vue";
export default defineComponent({
name: 'AmountField',
});
</script>
<template>
<v-text-field
variant = "outlined"
:clearable=false
density = "compact"
auto-grow
rows = "1"
:model-value = "props.amount"
v-maska = "options"
v-bind = "$attrs"
@maska = "(event) => {console.info(event.detail.unmasked); emit('update:modelValue',Number(event?.detail?.unmasked))}"
/>
</template>
<style scoped>
</style>
мои тестовые примеры выглядят так:
import AmountField from "@/components/elements/AmountField.vue";
describe('xxxx', (): void => {
it('no fraction', () => {
cy.mount(AmountField, {
propsData: {
amount: 26717,
editable: true,
id: "amountField"
}
} as any).then(({wrapper, component}) =>
{
cy.get("#amountField").find('input').should('have.value', '26.717').should('not.be.disabled')
cy.get("#amountField").find('input').type('{selectAll}1234,56').then(() => {
expect((wrapper).emitted('update:modelValue')).to.have.length;
expect((wrapper).emitted('update:modelValue')[7][0]).to.equal(1234.56)
})
});
})
it('with fraction', () => {
cy.mount(AmountField, {
propsData: {
betrag: 26717.01,
editable: true,
disabled: true,
id: "amountField"
}
} as any);
cy.get("#amountField").find('input').should('have.value', '26.717,01')
})
})
Это проблема в Maska (v3.0) с числовым вводом и форматом, который использует точку в качестве разделителя группировки (например, немецкий 1.000.000,99
). Даже если ввод не замаскирован, Маска начинает с удаления разделителя группировки. Таким образом, если входные данные представляют собой число с плавающей запятой, а разделителем группировки является точка, десятичная точка будет удалена. 26717.01
становится 2671701
, который форматируется как 2.671.701
.
Я предполагаю, что это непреднамеренное поведение. В качестве обходного пути вы можете начать с уже отформатированного значения:
<template>
<v-text-field
v-model = "maskedValue"
v-maska:amount.unmasked = "options"
/>
</template>
<script setup>
const amount = ref(26717.01)
const options = {...}
const preformattedValue = new Intl.NumberFormat(options.number.locale).format(amount.value)
const maskedValue = ref(preformattedValue)
</script>
Если amount
можно изменить снаружи, вам, вероятно, придется использовать наблюдатель для обновления форматированного значения.
const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = {
directives: {maska: window.Maska.vMaska},
template: `
<v-app>
<v-main>
<v-container>
<v-text-field
v-model = "maskedValue"
v-maska:amount.unmasked = "options"
/>
<pre>amount: {{amount}}</pre>
</v-container>
</v-main>
</v-app>
`,
setup(){
const options = {
number: {
fraction: 2,
locale: 'de',
},
reversed: true,
}
const formatter = new Intl.NumberFormat(options.number.locale)
const amount = ref(26717.01)
return {
amount,
maskedValue: ref(formatter.format(amount.value)),
options,
}
}
}
const a = createApp(app).use(vuetify).mount('#app')
<link rel = "stylesheet" type = "text/css" href = "https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css" />
<link href = "https://cdn.jsdelivr.net/npm/@mdi/font/css/materialdesignicons.min.css" rel = "stylesheet">
<div id = "app"></div>
<script src = "https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src = "https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js"></script>
<script src = "https://unpkg.com/[email protected]/dist/cdn/vue.js"></script>