tui-numberbox.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <template>
  2. <view class="tui-numberbox">
  3. <view class="tui-numbox-icon tui-icon-reduce " :class="[disabled || min >= inputValue ? 'tui-disabled' : '']"
  4. @tap="reduce" :style="{ color: iconColor, fontSize: iconSize + 'rpx' }"></view>
  5. <input type="number" v-model="inputValue" :disabled="disabled" @blur="blur" class="tui-num-input"
  6. :style="{ color: color, fontSize: size + 'rpx', backgroundColor: backgroundColor, height: height + 'rpx', minHeight: height + 'rpx', width: width + 'rpx' }" />
  7. <view class="tui-numbox-icon tui-icon-plus" :class="[disabled || inputValue >= max ? 'tui-disabled' : '']"
  8. @tap="plus" :style="{ color: iconColor, fontSize: iconSize + 'rpx' }"></view>
  9. </view>
  10. </template>
  11. <script>
  12. export default {
  13. name: 'tuiNumberbox',
  14. emits: ['change'],
  15. props: {
  16. value: {
  17. type: [Number, String],
  18. default: 1
  19. },
  20. //最小值
  21. min: {
  22. type: Number,
  23. default: 1
  24. },
  25. //最大值
  26. max: {
  27. type: Number,
  28. default: 99
  29. },
  30. //迈步大小 1 1.1 10...
  31. step: {
  32. type: Number,
  33. default: 1
  34. },
  35. //是否禁用操作
  36. disabled: {
  37. type: Boolean,
  38. default: false
  39. },
  40. //加减图标大小 rpx
  41. iconSize: {
  42. type: Number,
  43. default: 26
  44. },
  45. iconColor: {
  46. type: String,
  47. default: '#666666'
  48. },
  49. //input 高度
  50. height: {
  51. type: Number,
  52. default: 42
  53. },
  54. //input 宽度
  55. width: {
  56. type: Number,
  57. default: 80
  58. },
  59. size: {
  60. type: Number,
  61. default: 28
  62. },
  63. //input 背景颜色
  64. backgroundColor: {
  65. type: String,
  66. default: '#F5F5F5'
  67. },
  68. //input 字体颜色
  69. color: {
  70. type: String,
  71. default: '#333'
  72. },
  73. //索引值,列表中使用
  74. index: {
  75. type: [Number, String],
  76. default: 0
  77. },
  78. //自定义参数
  79. custom: {
  80. type: [Number, String],
  81. default: 0
  82. }
  83. },
  84. created() {
  85. this.inputValue = +this.value;
  86. },
  87. data() {
  88. return {
  89. inputValue: 0
  90. };
  91. },
  92. watch: {
  93. value(val) {
  94. this.inputValue = +val;
  95. }
  96. },
  97. methods: {
  98. getLen(val, step) {
  99. let len = 0;
  100. let lenVal = 0;
  101. //浮点型
  102. if (!Number.isInteger(step)) {
  103. len = (step + '').split('.')[1].length
  104. }
  105. //浮点型
  106. if (!Number.isInteger(val)) {
  107. lenVal = (val + '').split('.')[1].length
  108. }
  109. return Math.max(len, lenVal);
  110. },
  111. getScale(val, step) {
  112. let scale = 1;
  113. let scaleVal = 1;
  114. //浮点型
  115. if (!Number.isInteger(step)) {
  116. scale = Math.pow(10, (step + '').split('.')[1].length);
  117. }
  118. //浮点型
  119. if (!Number.isInteger(val)) {
  120. scaleVal = Math.pow(10, (val + '').split('.')[1].length);
  121. }
  122. return Math.max(scale, scaleVal);
  123. },
  124. calcNum: function(type) {
  125. if (this.disabled || (this.inputValue == this.min && type === 'reduce') || (this.inputValue == this
  126. .max && type === 'plus')) {
  127. return;
  128. }
  129. const scale = this.getScale(Number(this.inputValue), Number(this.step));
  130. let len = this.getLen(Number(this.inputValue), Number(this.step));
  131. let num = Number(this.inputValue) * scale;
  132. let step = this.step * scale;
  133. if (type === 'reduce') {
  134. num -= step;
  135. } else if (type === 'plus') {
  136. num += step;
  137. }
  138. let value = this.toFixed(num / scale, len);
  139. if (value < this.min) {
  140. value = this.min;
  141. } else if (value > this.max) {
  142. value = this.max;
  143. }
  144. this.handleChange(value, type);
  145. },
  146. plus: function() {
  147. this.calcNum('plus');
  148. },
  149. reduce: function() {
  150. this.calcNum('reduce');
  151. },
  152. blur: function(e) {
  153. let value = e.detail.value;
  154. if (value) {
  155. if (~value.indexOf('.') && Number.isInteger(this.step) && Number.isInteger(Number(value))) {
  156. value = value.split('.')[0];
  157. }
  158. value = Number(value);
  159. if (value > this.max) {
  160. value = this.max;
  161. } else if (value < this.min) {
  162. value = this.min;
  163. }
  164. } else {
  165. value = this.min;
  166. }
  167. if ((value == this.value && value != this.inputValue) || !e.detail.value) {
  168. this.inputValue = value;
  169. }
  170. this.handleChange(value, 'blur');
  171. },
  172. handleChange(value, type) {
  173. if (this.disabled) return;
  174. this.$emit('change', {
  175. value: Number(value),
  176. type: type,
  177. index: this.index,
  178. custom: this.custom
  179. });
  180. },
  181. toFixed(num, s) {
  182. let times = Math.pow(10, s)
  183. let des = num * times + 0.5
  184. des = parseInt(des, 10) / times
  185. return Number(des + '')
  186. }
  187. }
  188. };
  189. </script>
  190. <style scoped>
  191. @font-face {
  192. font-family: 'numberbox';
  193. src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAASQAA0AAAAABtwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAEdAAAABoAAAAciBpnRUdERUYAAARUAAAAHgAAAB4AKQALT1MvMgAAAZwAAABDAAAAVjxzSINjbWFwAAAB9AAAAEYAAAFK5zLpOGdhc3AAAARMAAAACAAAAAj//wADZ2x5ZgAAAkgAAACHAAAAnIfIEjxoZWFkAAABMAAAAC8AAAA2FZWEOWhoZWEAAAFgAAAAHAAAACQH3gOFaG10eAAAAeAAAAARAAAAEgwAAAFsb2NhAAACPAAAAAwAAAAMADAATm1heHAAAAF8AAAAHwAAACABEAAobmFtZQAAAtAAAAFJAAACiCnmEVVwb3N0AAAEHAAAAC0AAABV/+8iFXjaY2BkYGAA4gVmC5Tj+W2+MnCzMIDATWsFOQT9v5GFgbkeyOVgYAKJAgDrogf+AHjaY2BkYGBu+N/AEMPCAAJAkpEBFbAAAEcKAm142mNgZGBgYGWQYQDRDAxMQMwFhAwM/8F8BgALpAE5AHjaY2BkYWCcwMDKwMDUyXSGgYGhH0IzvmYwYuQAijKwMjNgBQFprikMDs9Yn01kbvjfwBDD3MDQABRmBMkBAOXpDHEAeNpjYYAAFghmZGAAAACdAA4AAAB42mNgYGBmgGAZBkYGEHAB8hjBfBYGDSDNBqQZGZiesT6b+P8/AwOElvwnWQxVDwSMbAxwDiMTkGBiQAWMDMMeAABRZwszAAAAAAAAAAAAAAAwAE542iWKQQrCMBBF5xNpd0pQ7EIoTEnahSCTUNqdWz2A9TrieXKeXCc1qcPn/zfzh0BYv2pVH7oQgbvqdG5Yt/DTrNlPYz+wHvuuqhFSME4sFshTgKUsKfhH5lg8BSul3i5bS3mQdd0RIh2IjnvUrkXDd8zuhuFt86tY9fonIsSYgsXpB+cCGosAeNp9kD1OAzEQhZ/zByQSQiCoXVEA2vyUKRMp9Ailo0g23pBo1155nUg5AS0VB6DlGByAGyDRcgpelkmTImvt6PObmeexAZzjGwr/3yXuhBWO8ShcwREy4Sr1F+Ea+V24jhY+hRvUf4SbuFUD4RYu1BsdVO2Eu5vSbcsKZxgIV3CKJ+Eq9ZVwjfwqXMcVPoQb1L+EmxjjV7iFa2WpDOFhMEFgnEFjig3jAjEcLJIyBtahOfRmEsxMTzd6ETubOBso71dilwMeaDnngCntPbdmvkon/mDLgdSYbh4FS7YpjS4idCgbXyyc1d2oc7D9nu22tNi/a4E1x+xRDWzU/D3bM9JIbAyvkJI18jK3pBJTj2hrrPG7ZynW814IiU68y/SIx5o0dTr3bmniwOLn8owcfbS5kj33qBw+Y1kIeb/dTsQgil2GP5PYcRkAAAB42mNgYoAALjDJyIAOWMGiTIxMjMwiWZmJQJRXVQoigTgjMd9QGIsgAFDsEBsAAAAAAAAB//8AAgABAAAADAAAABYAAAACAAEAAwAEAAEABAAAAAIAAAAAeNpjYGBgZACCq0vUOUD0TWsFORgNADPBBE4AAA==) format('woff');
  194. font-weight: normal;
  195. font-style: normal;
  196. }
  197. .tui-numbox-icon {
  198. font-family: 'numberbox' !important;
  199. font-style: normal;
  200. -webkit-font-smoothing: antialiased;
  201. -moz-osx-font-smoothing: grayscale;
  202. padding: 10rpx;
  203. }
  204. .tui-icon-reduce:before {
  205. content: '\e691';
  206. }
  207. .tui-icon-plus:before {
  208. content: '\e605';
  209. }
  210. .tui-numberbox {
  211. display: -webkit-inline-flex;
  212. display: inline-flex;
  213. align-items: center;
  214. }
  215. .tui-num-input {
  216. text-align: center;
  217. margin: 0 12rpx;
  218. font-weight: 400;
  219. }
  220. .tui-disabled {
  221. color: #ededed !important;
  222. }
  223. </style>