zhengyiming
2025-02-14 aec795ff58c4b76007f85aa95c734a8dfce83fe1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<template>
  <nut-input type="number" :formatter="formatter" formatTrigger="onBlur" v-model="innerModelValue">
    <template #right>
      <slot name="right"></slot>
    </template>
  </nut-input>
</template>
 
<script setup lang="ts">
import { computed } from 'vue';
 
defineOptions({
  name: 'NumberInput',
});
 
type Props = {
  min?: number;
  max?: number;
  precision?: number;
  modelValue?: number | string;
};
 
const props = withDefaults(defineProps<Props>(), {
  max: Math.pow(2, 53) - 1,
});
 
const emit = defineEmits<{
  (e: 'update:modelValue', val: string | number): void;
}>();
 
const innerModelValue = computed({
  get() {
    return props.modelValue + '';
  },
  set(val) {
    emit('update:modelValue', val);
  },
});
 
function formatter(value: string): string {
  const newVal = value !== '' ? Number.parseFloat(value) : '';
  if (Number.isNaN(newVal)) {
    return '';
  }
  if (newVal && newVal > props.max) {
    return `${toPrecision(props.max)}`;
  }
  if (props.min !== undefined && !!`${newVal}` && (newVal as number) < props.min) {
    return `${toPrecision(props.min)}`;
  }
  return newVal !== '' ? `${toPrecision(newVal)}` : newVal;
}
 
function toPrecision(num: number) {
  if (props.precision) {
    if (props.precision === 0) return Math.round(num);
    let snum = String(num);
    const pointPos = snum.indexOf('.');
    if (pointPos === -1) return num;
    const nums = snum.replace('.', '').split('');
    const datum = nums[pointPos + props.precision];
    if (!datum) return num;
    const length = snum.length;
    if (snum.charAt(length - 1) === '5') {
      snum = `${snum.slice(0, Math.max(0, length - 1))}6`;
    }
    return Number(snum).toFixed(props.precision);
  }
  return String(num);
}
</script>