华西海圻ELN前端工程
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

147 lines
4.3 KiB

  1. <template>
  2. <el-input v-bind="$attrs" v-model="internalValue" @input="handleInput" @blur="handleBlur" :placeholder="placeholder"
  3. :disabled="disabled" type="text">
  4. <template slot="prepend" v-if="prepend">
  5. {{ prepend }}
  6. </template>
  7. </el-input>
  8. </template>
  9. <script>
  10. export default {
  11. name: 'DecimalInput',
  12. props: {
  13. value: {
  14. type: [Number, String],
  15. default: ''
  16. },
  17. decimalDigits: {
  18. type: Number,
  19. default: 3
  20. },
  21. placeholder: {
  22. type: String,
  23. default: '请输入'
  24. },
  25. disabled: {
  26. type: Boolean,
  27. default: false
  28. },
  29. prepend: {
  30. type: String,
  31. default: ''
  32. },
  33. },
  34. data() {
  35. return {
  36. internalValue: this.value !== null && this.value !== undefined ? String(this.value) : ''
  37. };
  38. },
  39. watch: {
  40. value(newVal) {
  41. // 外部值变化时同步到内部(但不做格式化,避免干扰用户输入)
  42. console.log(newVal,"newVal")
  43. if (newVal === '' || newVal == null) {
  44. this.internalValue = '';
  45. } else {
  46. this.internalValue = String(newVal);
  47. }
  48. }
  49. },
  50. methods: {
  51. handleInput(val) {
  52. console.log(val,"val")
  53. if (val === '') {
  54. this.internalValue = '';
  55. this.$emit('input', '');
  56. return;
  57. }
  58. // 1. 只保留数字、小数点、开头的负号
  59. let cleaned = val
  60. .replace(/[^\d.-]/g, '')
  61. .replace(/^(-)\1+/, '$1'); // 合并多个负号
  62. // 2. 只保留第一个小数点
  63. const firstDotIndex = cleaned.indexOf('.');
  64. if (firstDotIndex !== -1) {
  65. const before = cleaned.slice(0, firstDotIndex);
  66. const after = cleaned.slice(firstDotIndex + 1).replace(/\./g, '');
  67. cleaned = before + '.' + after;
  68. }
  69. // 3. 限制小数位数(仅当允许小数时)
  70. if (this.decimalDigits > 0 && cleaned.includes('.')) {
  71. const [intPart, decPart = ''] = cleaned.split('.');
  72. cleaned = intPart + '.' + decPart.slice(0, this.decimalDigits);
  73. } else if (this.decimalDigits === 0) {
  74. cleaned = cleaned.split('.')[0]; // 移除小数部分
  75. }
  76. // 4. 处理以 . 或 -. 开头
  77. if (cleaned === '.') cleaned = '0.';
  78. else if (cleaned === '-.') cleaned = '-0.';
  79. else if (cleaned.startsWith('.')) cleaned = '0' + cleaned;
  80. else if (cleaned.startsWith('-.')) cleaned = '-0.' + cleaned.slice(2);
  81. // 5. 【关键】安全去除前导零,但保留单个 0
  82. if (cleaned.includes('.')) {
  83. // 有小数点:处理整数部分
  84. const [int, dec] = cleaned.split('.');
  85. // 整数部分:-0012 → -12,00 → 0,0 → 0,-0 → 0(或保留 -0)
  86. let newInt = int;
  87. if (/^-?0+\d/.test(int)) {
  88. newInt = int.replace(/^-?0+(\d)/, '$1');
  89. } else if (int === '' || int === '-') {
  90. newInt = int + '0';
  91. } else if (int === '00' || /^-00+$/.test(int)) {
  92. newInt = int.startsWith('-') ? '-0' : '0';
  93. }
  94. cleaned = newInt + '.' + dec;
  95. } else {
  96. // 无小数点
  97. if (/^-?0+\d/.test(cleaned)) {
  98. cleaned = cleaned.replace(/^-?0+(\d)/, '$1');
  99. } else if (cleaned === '00' || /^-00+$/.test(cleaned)) {
  100. cleaned = cleaned.startsWith('-') ? '-0' : '0';
  101. }
  102. // 注意:不要把单独的 '0' 变成 ''
  103. }
  104. this.internalValue = cleaned;
  105. // emit
  106. if (cleaned === '' || cleaned === '-') {
  107. this.$emit('input', cleaned === '-' ? '-' : '');
  108. } else {
  109. const num = parseFloat(cleaned);
  110. console.log(num,isNaN(num),"num")
  111. this.$emit('input', isNaN(num) ? '' : num);
  112. }
  113. },
  114. handleBlur() {
  115. let finalValue = this.internalValue.trim();
  116. if (finalValue === '' || finalValue === '-') {
  117. this.internalValue = '';
  118. this.$emit('input', '');
  119. return;
  120. }
  121. const num = parseFloat(finalValue);
  122. if (isNaN(num)) {
  123. this.internalValue = '';
  124. this.$emit('input', '');
  125. return;
  126. }
  127. // 在 blur 时才做 toFixed(四舍五入 + 补零)
  128. const formatted = num.toFixed(this.decimalDigits);
  129. this.internalValue = formatted;
  130. // emit 数字类型(也可 emit 字符串,根据需求)
  131. this.$emit('input', parseFloat(formatted));
  132. this.$emit('blur', parseFloat(formatted));
  133. }
  134. }
  135. };
  136. </script>