index.vue 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <template>
  2. <div
  3. class="m-input-number"
  4. :class="[
  5. { 'is-disabled': inputNumberDisabled },
  6. { 'is-controls-right': controlsAtRight },
  7. inputNumberSize ? 'm-input-number--' + inputNumberSize : '',
  8. ]">
  9. <span
  10. v-if="controls"
  11. class="m-input-number__decrease"
  12. :class="{'is-disabled':minDisabled}"
  13. role="button"
  14. @click="handleClick('decrease')"
  15. >
  16. <i class="iconfont" :class="[controlsAtRight ? 'icon-arrow-down-bold' : 'icon-minus']"></i>
  17. </span>
  18. <input
  19. type="text"
  20. class="m-input__inner"
  21. :name="name"
  22. :value="inputVal"
  23. :disabled="inputNumberDisabled"
  24. @change="handleInputChange"
  25. @blur="handleBlur"
  26. @focus="handleFocus">
  27. <span
  28. v-if="controls"
  29. class="m-input-number__increase"
  30. :class="{'is-disabled':maxDisabled}"
  31. role="button"
  32. @click="handleClick('increase')"
  33. >
  34. <i class="iconfont" :class="[controlsAtRight ? 'icon-arrow-up-bold' : 'icon-add']"></i>
  35. </span>
  36. </div>
  37. </template>
  38. <script>
  39. export default {
  40. name: "mInputNumber"
  41. };
  42. </script>
  43. <script setup>
  44. import { ref,computed, getCurrentInstance } from 'vue-demi';
  45. const emit = defineEmits(['update:modelValue','change','focus','blur'])
  46. const props = defineProps({
  47. modelValue: [Number,String],
  48. name: String,
  49. step: {
  50. type: Number,
  51. default: 1
  52. },
  53. max: {
  54. type: Number,
  55. default: Infinity
  56. },
  57. min: {
  58. type: Number,
  59. default: -Infinity
  60. },
  61. disabled: {
  62. type: Boolean,
  63. default: false
  64. },
  65. stepStrictly: {
  66. type: Boolean,
  67. default: false
  68. },
  69. precision: {
  70. type: Number,
  71. validator(val) {
  72. return val >= 0 && val === parseInt(val, 10);
  73. }
  74. },
  75. controls: {
  76. type: Boolean,
  77. default: true
  78. },
  79. controlsPosition: {
  80. type: String,
  81. default: ''
  82. },
  83. size: String
  84. })
  85. const instance = getCurrentInstance();
  86. const inputVal = computed({
  87. get: () => props.modelValue,
  88. set: (newVal) => {
  89. let {min,max} = props
  90. let limit = [
  91. {
  92. validate: (value) => value <= min,
  93. res: min
  94. },
  95. {
  96. validate: (value) => value >= max,
  97. res: max
  98. },
  99. {
  100. validate: value => true,
  101. res: newVal * 1
  102. }
  103. ]
  104. let _val = limit.find(v => v.validate(newVal)).res
  105. // console.log('_val',_val);
  106. if(props.precision) {
  107. emit('update:modelValue',_val.toFixed(props.precision))
  108. emit('change',_val.toFixed(props.precision))
  109. } else {
  110. emit('update:modelValue',_val)
  111. emit('change',_val)
  112. }
  113. }
  114. })
  115. const controlsAtRight = computed(() => {
  116. return props.controls && props.controlsPosition === 'right'
  117. })
  118. const inputNumberSize = computed(() => {
  119. return props.size
  120. })
  121. const minDisabled = computed(() => {
  122. return props.disabled || props.modelValue <= props.min
  123. })
  124. const maxDisabled = computed(() => {
  125. return props.disabled || props.modelValue >= props.max
  126. })
  127. const inputNumberDisabled = computed(() => {
  128. return props.disabled
  129. })
  130. const handleInputChange = (e) => {
  131. let value = e.target.value;
  132. const newVal = Number(value);
  133. if (!isNaN(newVal) || value === '') {
  134. setCurrentValue(newVal)
  135. } else {
  136. // 如果输入的非数字,则保留之前的数据
  137. e.target.value = inputVal.value
  138. }
  139. }
  140. const setCurrentValue = (val) => {
  141. const newVal = verifyValue(val)
  142. emit('update:modelValue',newVal)
  143. emit('change',newVal)
  144. // 针对只有第一次改值生效的问题
  145. instance.proxy.$forceUpdate();
  146. }
  147. const verifyValue = (value) => {
  148. const { min, max, step, precision, stepStrictly } = props
  149. let newVal = Number(value)
  150. if(newVal <= min) newVal = min
  151. if(newVal >= max) newVal = max
  152. if (stepStrictly) {
  153. newVal = Math.round(newVal / step) * step
  154. }
  155. return newVal
  156. }
  157. const handleClick = (type) => {
  158. if(props.disabled) return
  159. if(type === 'decrease') {
  160. inputVal.value = Number(inputVal.value) - props.step
  161. } else {
  162. inputVal.value = Number(inputVal.value) + props.step
  163. }
  164. }
  165. const handleBlur = (e) => {
  166. emit('blur',e)
  167. }
  168. const handleFocus = (e) => {
  169. emit('focus',e)
  170. }
  171. </script>
  172. <style lang="scss" scoped>
  173. @import '../../styles/components/input-number.scss';
  174. </style>