tui-numberbox.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <view class="tui-numberbox">
  3. <view class="tui-num__icon__box" :style="{background:iconBgColor,borderRadius:radius}" @tap="reduce"
  4. :class="[disabled || min >= inputValue ? 'tui-disabled' : '']">
  5. <text class="tui-numbox-icon tui-num__icon-reduce"
  6. :style="{ color: iconColor, fontSize: iconSize + 'rpx',lineHeight:iconSize + 'rpx' }"></text>
  7. </view>
  8. <input type="number" v-model="inputValue" :disabled="disabled" @blur="blur" class="tui-num-input"
  9. :style="{ color: color, fontSize: size + 'rpx', background: backgroundColor, height: height + 'rpx', minHeight: height + 'rpx', width: width + 'rpx' }" />
  10. <view class="tui-num__icon__box" :style="{background:iconBgColor,borderRadius:radius}" @tap="plus"
  11. :class="[disabled || inputValue >= max ? 'tui-disabled' : '']">
  12. <text class="tui-numbox-icon tui-num__icon-plus"
  13. :style="{ color: iconColor, fontSize: iconSize + 'rpx',lineHeight:iconSize + 'rpx' }"></text>
  14. </view>
  15. </view>
  16. </template>
  17. <script>
  18. export default {
  19. name: 'tuiNumberbox',
  20. emits: ['change'],
  21. props: {
  22. value: {
  23. type: [Number, String],
  24. default: 1
  25. },
  26. //最小值
  27. min: {
  28. type: Number,
  29. default: 1
  30. },
  31. //最大值
  32. max: {
  33. type: Number,
  34. default: 99
  35. },
  36. //迈步大小 1 1.1 10...
  37. step: {
  38. type: Number,
  39. default: 1
  40. },
  41. //是否禁用操作
  42. disabled: {
  43. type: Boolean,
  44. default: false
  45. },
  46. iconBgColor: {
  47. type: String,
  48. default: 'transparent'
  49. },
  50. radius:{
  51. type: String,
  52. default: '50%'
  53. },
  54. //加减图标大小 rpx
  55. iconSize: {
  56. type: Number,
  57. default: 22
  58. },
  59. iconColor: {
  60. type: String,
  61. default: '#666666'
  62. },
  63. //input 高度
  64. height: {
  65. type: Number,
  66. default: 42
  67. },
  68. //input 宽度
  69. width: {
  70. type: Number,
  71. default: 80
  72. },
  73. size: {
  74. type: Number,
  75. default: 28
  76. },
  77. //input 背景颜色
  78. backgroundColor: {
  79. type: String,
  80. default: '#F5F5F5'
  81. },
  82. //input 字体颜色
  83. color: {
  84. type: String,
  85. default: '#333'
  86. },
  87. //索引值,列表中使用
  88. index: {
  89. type: [Number, String],
  90. default: 0
  91. },
  92. //自定义参数
  93. custom: {
  94. type: [Number, String],
  95. default: 0
  96. }
  97. },
  98. created() {
  99. this.inputValue = +this.value;
  100. },
  101. data() {
  102. return {
  103. inputValue: 0
  104. };
  105. },
  106. watch: {
  107. value(val) {
  108. this.inputValue = +val;
  109. }
  110. },
  111. methods: {
  112. getScale(val, step) {
  113. let scale = 1;
  114. let scaleVal = 1;
  115. //浮点型
  116. if (!Number.isInteger(step)) {
  117. scale = Math.pow(10, (step + '').split('.')[1].length);
  118. }
  119. //浮点型
  120. if (!Number.isInteger(val)) {
  121. scaleVal = Math.pow(10, (val + '').split('.')[1].length);
  122. }
  123. return Math.max(scale, scaleVal);
  124. },
  125. calcNum: function(type) {
  126. if (this.disabled || (this.inputValue == this.min && type === 'reduce') || (this.inputValue == this
  127. .max && type === 'plus')) {
  128. return;
  129. }
  130. const scale = this.getScale(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 = Number((num / scale).toFixed(String(scale).length - 1));
  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. }
  182. };
  183. </script>
  184. <style scoped>
  185. @font-face {
  186. font-family: 'numberbox';
  187. 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');
  188. font-weight: normal;
  189. font-style: normal;
  190. }
  191. .tui-num__icon__box {
  192. display: flex;
  193. align-items: center;
  194. justify-content: center;
  195. text-align: center;
  196. padding: 12rpx;
  197. /* #ifdef H5 */
  198. cursor: pointer;
  199. /* #endif */
  200. }
  201. .tui-numbox-icon {
  202. font-family: 'numberbox' !important;
  203. font-style: normal;
  204. -webkit-font-smoothing: antialiased;
  205. -moz-osx-font-smoothing: grayscale;
  206. }
  207. .tui-num__icon-reduce:before {
  208. content: '\e691';
  209. }
  210. .tui-num__icon-plus:before {
  211. content: '\e605';
  212. }
  213. .tui-numberbox {
  214. /* #ifndef APP-NVUE */
  215. display: inline-flex;
  216. /* #endif */
  217. align-items: center;
  218. }
  219. .tui-num-input {
  220. text-align: center;
  221. margin: 0 6rpx;
  222. font-weight: 400;
  223. padding: 0;
  224. }
  225. .tui-disabled {
  226. opacity: .5;
  227. /* #ifdef H5 */
  228. cursor: not-allowed;
  229. /* #endif */
  230. }
  231. </style>