uni-number-box.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <template>
  2. <view class="uni-numbox">
  3. <view @click="_calcValue('minus')" class="uni-numbox__minus uni-numbox-btns" :style="{background}">
  4. <text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue <= min || disabled }"
  5. :style="{color}">-</text>
  6. </view>
  7. <input :disabled="disabled" @focus="_onFocus" @blur="_onBlur" class="uni-numbox__value"
  8. :type="step<1?'digit':'number'" v-model="inputValue" :style="{background, color, width:widthWithPx}" />
  9. <view @click="_calcValue('plus')" class="uni-numbox__plus uni-numbox-btns" :style="{background}">
  10. <text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue >= max || disabled }"
  11. :style="{color}">+</text>
  12. </view>
  13. </view>
  14. </template>
  15. <script>
  16. /**
  17. * NumberBox 数字输入框
  18. * @description 带加减按钮的数字输入框
  19. * @tutorial https://ext.dcloud.net.cn/plugin?id=31
  20. * @property {Number} value 输入框当前值
  21. * @property {Number} min 最小值
  22. * @property {Number} max 最大值
  23. * @property {Number} step 每次点击改变的间隔大小
  24. * @property {String} background 背景色
  25. * @property {String} color 字体颜色(前景色)
  26. * @property {Number} width 输入框宽度(单位:px)
  27. * @property {Boolean} disabled = [true|false] 是否为禁用状态
  28. * @event {Function} change 输入框值改变时触发的事件,参数为输入框当前的 value
  29. * @event {Function} focus 输入框聚焦时触发的事件,参数为 event 对象
  30. * @event {Function} blur 输入框失焦时触发的事件,参数为 event 对象
  31. */
  32. export default {
  33. name: "UniNumberBox",
  34. emits: ['change', 'input', 'update:modelValue', 'blur', 'focus'],
  35. props: {
  36. value: {
  37. type: [Number, String],
  38. default: 1
  39. },
  40. modelValue: {
  41. type: [Number, String],
  42. default: 1
  43. },
  44. min: {
  45. type: Number,
  46. default: 0
  47. },
  48. max: {
  49. type: Number,
  50. default: 100
  51. },
  52. step: {
  53. type: Number,
  54. default: 1
  55. },
  56. background: {
  57. type: String,
  58. default: '#f5f5f5'
  59. },
  60. color: {
  61. type: String,
  62. default: '#333'
  63. },
  64. disabled: {
  65. type: Boolean,
  66. default: false
  67. },
  68. width: {
  69. type: Number,
  70. default: 40,
  71. }
  72. },
  73. data() {
  74. return {
  75. inputValue: 0
  76. };
  77. },
  78. watch: {
  79. value(val) {
  80. this.inputValue = +val;
  81. },
  82. modelValue(val) {
  83. this.inputValue = +val;
  84. }
  85. },
  86. computed: {
  87. widthWithPx() {
  88. return this.width + 'px';
  89. }
  90. },
  91. created() {
  92. if (this.value === 1) {
  93. this.inputValue = +this.modelValue;
  94. }
  95. if (this.modelValue === 1) {
  96. this.inputValue = +this.value;
  97. }
  98. },
  99. methods: {
  100. _calcValue(type) {
  101. if (this.disabled) {
  102. return;
  103. }
  104. const scale = this._getDecimalScale();
  105. let value = this.inputValue * scale;
  106. let step = this.step * scale;
  107. if (type === "minus") {
  108. value -= step;
  109. if (value < (this.min * scale)) {
  110. return;
  111. }
  112. if (value > (this.max * scale)) {
  113. value = this.max * scale
  114. }
  115. }
  116. if (type === "plus") {
  117. value += step;
  118. if (value > (this.max * scale)) {
  119. return;
  120. }
  121. if (value < (this.min * scale)) {
  122. value = this.min * scale
  123. }
  124. }
  125. this.inputValue = (value / scale).toFixed(String(scale).length - 1);
  126. // TODO vue2 兼容
  127. this.$emit("input", +this.inputValue);
  128. // TODO vue3 兼容
  129. this.$emit("update:modelValue", +this.inputValue);
  130. this.$emit("change", +this.inputValue);
  131. },
  132. _getDecimalScale() {
  133. let scale = 1;
  134. // 浮点型
  135. if (~~this.step !== this.step) {
  136. scale = Math.pow(10, String(this.step).split(".")[1].length);
  137. }
  138. return scale;
  139. },
  140. _onBlur(event) {
  141. this.$emit('blur', event)
  142. let value = event.detail.value;
  143. if (isNaN(value)) {
  144. this.inputValue = this.min;
  145. return;
  146. }
  147. value = +value;
  148. if (value > this.max) {
  149. value = this.max;
  150. } else if (value < this.min) {
  151. value = this.min;
  152. }
  153. const scale = this._getDecimalScale();
  154. this.inputValue = value.toFixed(String(scale).length - 1);
  155. this.$emit("input", +this.inputValue);
  156. this.$emit("update:modelValue", +this.inputValue);
  157. this.$emit("change", +this.inputValue);
  158. },
  159. _onFocus(event) {
  160. this.$emit('focus', event)
  161. }
  162. }
  163. };
  164. </script>
  165. <style lang="scss">
  166. $box-height: 26px;
  167. $bg: #f5f5f5;
  168. $br: 2px;
  169. $color: #333;
  170. .uni-numbox {
  171. /* #ifndef APP-NVUE */
  172. display: flex;
  173. /* #endif */
  174. flex-direction: row;
  175. }
  176. .uni-numbox-btns {
  177. /* #ifndef APP-NVUE */
  178. display: flex;
  179. /* #endif */
  180. flex-direction: row;
  181. align-items: center;
  182. justify-content: center;
  183. padding: 0 8px;
  184. background-color: $bg;
  185. /* #ifdef H5 */
  186. cursor: pointer;
  187. /* #endif */
  188. }
  189. .uni-numbox__value {
  190. margin: 0 2px;
  191. background-color: $bg;
  192. width: 40px;
  193. height: $box-height;
  194. text-align: center;
  195. font-size: 14px;
  196. border-left-width: 0;
  197. border-right-width: 0;
  198. color: $color;
  199. }
  200. .uni-numbox__minus {
  201. border-top-left-radius: $br;
  202. border-bottom-left-radius: $br;
  203. }
  204. .uni-numbox__plus {
  205. border-top-right-radius: $br;
  206. border-bottom-right-radius: $br;
  207. }
  208. .uni-numbox--text {
  209. // fix nvue
  210. line-height: 20px;
  211. margin-bottom: 2px;
  212. font-size: 20px;
  213. font-weight: 300;
  214. color: $color;
  215. }
  216. .uni-numbox .uni-numbox--disabled {
  217. color: #c0c0c0;
  218. /* #ifdef H5 */
  219. cursor: not-allowed;
  220. /* #endif */
  221. }
  222. </style>