@netang/quasar 0.0.102 → 0.0.103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +11 -11
  3. package/_docs/docs/.vuepress/client.js +8 -8
  4. package/_docs/docs/.vuepress/config.js +40 -40
  5. package/_docs/docs/.vuepress/configs/index.js +2 -2
  6. package/_docs/docs/.vuepress/configs/navbar/index.js +1 -1
  7. package/_docs/docs/.vuepress/configs/navbar/zh.js +16 -16
  8. package/_docs/docs/.vuepress/configs/sidebar/index.js +1 -1
  9. package/_docs/docs/.vuepress/configs/sidebar/zh.js +75 -75
  10. package/_docs/docs/.vuepress/public/css/index.css +3 -3
  11. package/_docs/docs/.vuepress/styles/index.scss +3 -3
  12. package/_docs/docs/components/column-title.md +25 -25
  13. package/_docs/docs/components/data.md +66 -66
  14. package/_docs/docs/components/dialog.md +59 -59
  15. package/_docs/docs/components/dragger.md +26 -26
  16. package/_docs/docs/components/editor-code.md +16 -16
  17. package/_docs/docs/components/empty.md +13 -13
  18. package/_docs/docs/components/field-date.md +16 -16
  19. package/_docs/docs/components/field-text.md +57 -57
  20. package/_docs/docs/components/field-tree.md +14 -14
  21. package/_docs/docs/components/img.md +25 -25
  22. package/_docs/docs/components/input-number.md +21 -21
  23. package/_docs/docs/components/list-menu-item.md +21 -21
  24. package/_docs/docs/components/list-menu.md +21 -21
  25. package/_docs/docs/components/power-page.md +21 -21
  26. package/_docs/docs/components/price.md +21 -21
  27. package/_docs/docs/components/render.md +12 -12
  28. package/_docs/docs/components/search-item.md +10 -10
  29. package/_docs/docs/components/search.md +12 -12
  30. package/_docs/docs/components/select.md +11 -11
  31. package/_docs/docs/components/splitter.md +15 -15
  32. package/_docs/docs/components/table-column-fixed.md +20 -20
  33. package/_docs/docs/components/table-pagination.md +20 -20
  34. package/_docs/docs/components/table-splitter.md +20 -20
  35. package/_docs/docs/components/table-summary.md +20 -20
  36. package/_docs/docs/components/table.md +25 -25
  37. package/_docs/docs/components/thumbnail.md +18 -18
  38. package/_docs/docs/components/toolbar.md +9 -9
  39. package/_docs/docs/components/uploader-query.md +19 -19
  40. package/_docs/docs/components/uploader.md +16 -16
  41. package/_docs/docs/components/value-format.md +26 -26
  42. package/_docs/docs/index.md +1 -1
  43. package/_docs/docs/utils/alert.md +26 -26
  44. package/_docs/docs/utils/area.md +112 -112
  45. package/_docs/docs/utils/arr.md +80 -80
  46. package/_docs/docs/utils/auth.md +101 -101
  47. package/_docs/docs/utils/bus.md +18 -18
  48. package/_docs/docs/utils/confirm.md +31 -31
  49. package/_docs/docs/utils/copy.md +22 -22
  50. package/_docs/docs/utils/dialog.md +98 -98
  51. package/_docs/docs/utils/dict.md +50 -50
  52. package/_docs/docs/utils/dictOptions.md +27 -27
  53. package/_docs/docs/utils/form.md +33 -33
  54. package/_docs/docs/utils/getData.md +60 -60
  55. package/_docs/docs/utils/getFile.md +21 -21
  56. package/_docs/docs/utils/getImage.md +33 -33
  57. package/_docs/docs/utils/getTime.md +51 -51
  58. package/_docs/docs/utils/index.md +1 -1
  59. package/_docs/docs/utils/loading.md +18 -18
  60. package/_docs/docs/utils/notify.md +29 -29
  61. package/_docs/docs/utils/power.md +353 -353
  62. package/_docs/docs/utils/previewImage.md +11 -11
  63. package/_docs/docs/utils/price.md +45 -45
  64. package/_docs/docs/utils/rule.md +30 -30
  65. package/_docs/docs/utils/ruleValid.md +31 -31
  66. package/_docs/docs/utils/symbols.md +30 -30
  67. package/_docs/docs/utils/table.md +194 -194
  68. package/_docs/docs/utils/timestamp.md +27 -27
  69. package/_docs/docs/utils/toast.md +27 -27
  70. package/_docs/docs/utils/tree.md +174 -174
  71. package/_docs/docs/utils/uploader.md +29 -29
  72. package/_docs/package.json +11 -11
  73. package/components/column-title/index.vue +37 -37
  74. package/components/data/index.vue +20 -20
  75. package/components/dialog/index.vue +372 -372
  76. package/components/dragger/index.vue +203 -203
  77. package/components/drawer/index.vue +303 -303
  78. package/components/editor-code/index.vue +289 -289
  79. package/components/empty/index.vue +71 -71
  80. package/components/field-date/index.vue +850 -850
  81. package/components/field-date/methods.js +100 -100
  82. package/components/field-table/index.vue +1222 -1222
  83. package/components/field-text/index.vue +165 -165
  84. package/components/field-tree/index.vue +103 -81
  85. package/components/img/index.vue +202 -202
  86. package/components/input-number/index.vue +546 -546
  87. package/components/list-menu/index.vue +149 -149
  88. package/components/list-menu-item/index.vue +79 -79
  89. package/components/power-page/index.vue +92 -92
  90. package/components/price/index.vue +188 -188
  91. package/components/private/components/index.js +11 -11
  92. package/components/private/components/move-to-tree/index.vue +154 -154
  93. package/components/private/edit-power-data/index.vue +816 -816
  94. package/components/private/table-visible-columns-button/index.vue +109 -109
  95. package/components/render/index.vue +150 -150
  96. package/components/search/index.vue +222 -222
  97. package/components/search-item/index.vue +210 -210
  98. package/components/splitter/index.vue +415 -415
  99. package/components/table/index.vue +456 -456
  100. package/components/table-column-fixed/index.vue +112 -112
  101. package/components/table-pagination/index.vue +192 -192
  102. package/components/table-splitter/index.vue +360 -360
  103. package/components/table-summary/index.vue +110 -110
  104. package/components/thumbnail/index.vue +72 -72
  105. package/components/toolbar/container.vue +31 -31
  106. package/components/toolbar/index.vue +136 -136
  107. package/components/uploader/index.vue +158 -158
  108. package/components/uploader-query/index.vue +758 -758
  109. package/components/value-format/index.vue +274 -274
  110. package/configs/area3.js +1 -1
  111. package/docs/css/index.css +3 -3
  112. package/package.json +1 -1
  113. package/sass/common.scss +174 -174
  114. package/sass/index.scss +14 -14
  115. package/sass/line.scss +39 -39
  116. package/sass/quasar/btn.scss +46 -46
  117. package/sass/quasar/common.scss +3 -3
  118. package/sass/quasar/dialog.scss +7 -7
  119. package/sass/quasar/drawer.scss +6 -6
  120. package/sass/quasar/field.scss +243 -243
  121. package/sass/quasar/loading.scss +6 -6
  122. package/sass/quasar/menu.scss +8 -8
  123. package/sass/quasar/table.scss +150 -150
  124. package/sass/quasar/toolbar.scss +22 -22
  125. package/store/index.js +29 -29
  126. package/utils/$auth.js +127 -127
  127. package/utils/$form.js +56 -56
  128. package/utils/$power.js +1215 -1215
  129. package/utils/$rule.js +13 -13
  130. package/utils/$ruleValid.js +10 -10
  131. package/utils/$table.js +999 -999
  132. package/utils/$tree.js +713 -713
  133. package/utils/alert.js +12 -12
  134. package/utils/area.js +400 -400
  135. package/utils/arr.js +51 -51
  136. package/utils/bus.js +6 -6
  137. package/utils/config.js +52 -52
  138. package/utils/confirm.js +11 -11
  139. package/utils/copy.js +30 -30
  140. package/utils/dialog.js +36 -36
  141. package/utils/dict.js +21 -21
  142. package/utils/dictOptions.js +28 -28
  143. package/utils/getData.js +73 -73
  144. package/utils/getFile.js +40 -40
  145. package/utils/getImage.js +153 -153
  146. package/utils/getTime.js +106 -106
  147. package/utils/index.js +61 -61
  148. package/utils/loading.js +15 -15
  149. package/utils/notify.js +13 -13
  150. package/utils/previewImage.js +10 -10
  151. package/utils/price.js +18 -18
  152. package/utils/symbols.js +18 -18
  153. package/utils/timestamp.js +18 -18
  154. package/utils/toast.js +13 -13
  155. package/utils/uploader/aliyun.js +6 -6
  156. package/utils/uploader/local.js +8 -8
  157. package/utils/uploader/qiniu.js +321 -321
  158. package/utils/uploader.js +1059 -1059
  159. package/utils/useAuth.js +30 -30
  160. package/utils/useRouter.js +47 -47
  161. package/utils/useSearch.js +0 -6
  162. package/utils/useUploader.js +53 -53
@@ -1,546 +1,546 @@
1
- <template>
2
- <q-input
3
- class="n-input-number"
4
- :class="{
5
- 'n-input-number--center': center,
6
- }"
7
- :disable="disable"
8
- :readonly="readonly"
9
- v-model="currentValue"
10
- @blur="onBlur"
11
- v-bind="$attrs"
12
- >
13
- <!-- 内部前置插槽 -->
14
- <template #prepend v-if="controls && center">
15
-
16
- <!-- 减少按钮 -->
17
- <q-btn
18
- class="n-input-number__left"
19
- color="default"
20
- icon="remove"
21
- flat
22
- dense
23
- :disable="currentDisableMinus"
24
- @click="onChange('minus')"
25
- />
26
-
27
- <!-- 内部前置插槽 -->
28
- <slot name="prepend" />
29
-
30
- </template>
31
-
32
- <!-- 内部后置插槽 -->
33
- <template #append v-if="controls">
34
-
35
- <!-- 内部后置插槽 -->
36
- <slot name="append" />
37
-
38
- <!-- 减少按钮 -->
39
- <q-btn
40
- color="default"
41
- icon="remove"
42
- flat
43
- dense
44
- :disable="currentDisableMinus"
45
- @click="onChange('minus')"
46
- v-if="! center"
47
- />
48
-
49
- <!-- 增加按钮 -->
50
- <q-btn
51
- class="n-input-number__right"
52
- color="default"
53
- icon="add"
54
- flat
55
- dense
56
- :disable="currentDisablePlus"
57
- @click="onChange('plus')"
58
- />
59
-
60
- </template>
61
-
62
- <!-- 插槽 -->
63
- <template
64
- v-for="slotName in slotNames"
65
- v-slot:[slotName]
66
- >
67
- <slot :name="slotName" />
68
- </template>
69
-
70
- </q-input>
71
- </template>
72
-
73
- <script>
74
- import { computed, ref, watch } from 'vue'
75
- import BigNumber from 'bignumber.js'
76
-
77
- import $n_filter from 'lodash/filter'
78
-
79
- import $n_isValidObject from '@netang/utils/isValidObject'
80
-
81
- export default {
82
-
83
- /**
84
- * 标识
85
- */
86
- name: 'NInputNumber',
87
-
88
- /**
89
- * 声明属性
90
- */
91
- props: {
92
- // 值 v-model
93
- modelValue: {
94
- required: true,
95
- },
96
- // 最小值
97
- min: {
98
- type: [Number, String],
99
- default: 0,
100
- },
101
- // 最大值
102
- max: {
103
- type: [Number, String],
104
- default: Infinity,
105
- },
106
- // 步长, 每次点击时改变的值(默认为 1, centToYuan开启后默认为 100)
107
- step: [Number, String],
108
- // 小数位数(默认为 0, centToYuan开启后默认为 2)
109
- decimalLength: [Number, String],
110
- // 是否禁用减少按钮
111
- disableMinus: Boolean,
112
- // 是否禁用增加按钮
113
- disablePlus: Boolean,
114
- // 是否使用控制按钮
115
- controls: Boolean,
116
- // 居中显示
117
- center: Boolean,
118
- // 不允许输入的值为空
119
- noEmpty: Boolean,
120
- // 是否为人民币的分转元(值为分, 显示为元)
121
- // 如果为 true, 则 min / max / step 的值默认的单位为人民币的分
122
- centToYuan: Boolean,
123
- // 精度舍入模式(默认: 向下取整)
124
- // 参考文档: https://mikemcl.github.io/bignumber.js/#constructor-properties
125
- roundMode: {
126
- type: Number,
127
- default: BigNumber.ROUND_DOWN,
128
- },
129
- // 是否禁用
130
- disable: Boolean,
131
- // 是否只读
132
- readonly: Boolean,
133
- },
134
-
135
- /**
136
- * 声明事件
137
- */
138
- emits: [
139
- 'update:modelValue',
140
- 'blur',
141
- 'minus',
142
- 'plus',
143
- ],
144
-
145
- /**
146
- * 组合式
147
- */
148
- setup(props, { emit, slots }) {
149
-
150
- // ==========【计算属性】=========================================================================================
151
-
152
- /**
153
- * 插槽标识
154
- */
155
- const slotNames = computed(function() {
156
-
157
- if ($n_isValidObject(slots)) {
158
-
159
- // 忽略插槽
160
- const ignoreKeys = []
161
-
162
- if (props.controls) {
163
- ignoreKeys.push('append')
164
-
165
- if (props.center) {
166
- ignoreKeys.push('prepend')
167
- }
168
- }
169
-
170
- const keys = Object.keys(slots)
171
-
172
- if (ignoreKeys.length) {
173
- return $n_filter(keys, e => ignoreKeys.indexOf(e) === -1)
174
- }
175
-
176
- return keys
177
- }
178
-
179
- return []
180
- })
181
-
182
- /**
183
- * 当前最小值
184
- */
185
- const currentMin = computed(function() {
186
- // 格式化数字
187
- return formatNumber(props.min, true, true, null)
188
- })
189
-
190
- /**
191
- * 当前最大值
192
- */
193
- const currentMax = computed(function() {
194
-
195
- // 如果为无限大
196
- if (props.max === Infinity) {
197
- // 则返回无限大
198
- return Infinity
199
- }
200
-
201
- // 格式化数字
202
- return formatNumber(props.max, true, true, null)
203
- })
204
-
205
- /**
206
- * 当前步长(默认为 1, centToYuan开启后默认为 100)
207
- */
208
- const currentStep = computed(function() {
209
-
210
- // 格式化数字
211
- return formatNumber(props.step ?? (props.centToYuan ? 100 : 1), true, true, null)
212
- })
213
-
214
- /**
215
- * 当前小数位数(默认为 0, centToYuan开启后默认为 2)
216
- */
217
- const currentDecimalLength = computed(function() {
218
- return props.decimalLength ?? (props.centToYuan ? 2 : 0)
219
- })
220
-
221
- /**
222
- * 当前是否禁用减少按钮
223
- */
224
- const currentDisableMinus = computed(function () {
225
-
226
- // 如果禁用 || 如果只读 || 禁用减少按钮
227
- if (props.disable || props.readonly || props.disableMinus) {
228
- // 则禁用减少按钮
229
- return true
230
- }
231
-
232
- // 如果没有当前最小值
233
- if (currentMin.value === null) {
234
- // 则不禁用减少按钮
235
- return false
236
- }
237
-
238
- // 将当前值转为 BigNumber 类型
239
- const val = new BigNumber(currentValue.value)
240
-
241
- // 如果当前值不是有效数字
242
- if (! val.isFinite()) {
243
- // 则禁用减少按钮
244
- return true
245
- }
246
-
247
- // 当前值 <= 当前最小值
248
- return val.lte(currentMin.value)
249
- })
250
-
251
- /**
252
- * 当前是否禁用增加按钮
253
- */
254
- const currentDisablePlus = computed(function () {
255
-
256
- // 如果禁用 || 如果只读 || 禁用增加按钮
257
- if (props.disable || props.readonly || props.disablePlus) {
258
- // 则禁用增加按钮
259
- return true
260
- }
261
-
262
- // 如果没有当前最大值
263
- if (currentMax.value === null) {
264
- // 则不禁用增加按钮
265
- return false
266
- }
267
-
268
- // 将当前值转为 BigNumber 类型
269
- const val = new BigNumber(currentValue.value)
270
-
271
- // 如果当前值不是有效数字
272
- if (! val.isFinite()) {
273
- // 则禁用减少按钮
274
- return true
275
- }
276
-
277
- // 当前值 >= 当前最大值
278
- return val.gte(currentMax.value)
279
- })
280
-
281
- // ==========【数据】============================================================================================
282
-
283
- // 格式化为当前值
284
- const currentValue = ref(formatToCurrentValue(props.modelValue, true))
285
-
286
- // 如果当前值 !== 声明值
287
- const rawModelValue = formatToModelValue(currentValue.value)
288
- if (rawModelValue !== props.modelValue) {
289
-
290
- // 触发更新值
291
- emitModelValue(rawModelValue)
292
- }
293
-
294
- // ==========【监听数据】=========================================================================================
295
-
296
- /**
297
- * 监听声明值
298
- */
299
- watch(() => props.modelValue, function (val) {
300
-
301
- // 格式化为当前值
302
- val = formatToCurrentValue(val, true)
303
-
304
- // 如果当前值有变化
305
- if (val !== currentValue.value) {
306
- currentValue.value = val
307
- }
308
- })
309
-
310
- /**
311
- * 监听 当前最小值 / 当前最大值 / 当前小数位数
312
- */
313
- watch([currentMin, currentMax, currentDecimalLength], function () {
314
-
315
- // 格式化当前值
316
- const val = formatToCurrentValue(currentValue.value, false)
317
-
318
- // 如果当前值有变化
319
- if (val !== currentValue.value) {
320
-
321
- // 更新当前值
322
- currentValue.value = val
323
-
324
- // 更新值
325
- emitModelValue(formatToModelValue(val))
326
- }
327
- })
328
-
329
- // ==========【方法】=============================================================================================
330
-
331
- /**
332
- * 触发更新值
333
- */
334
- function emitModelValue(val) {
335
-
336
- // 触发更新值
337
- emit('update:modelValue', val)
338
- }
339
-
340
- /**
341
- * 格式化数字
342
- */
343
- function formatNumber(val, isCentToYuan, isToNumber, defaultValue) {
344
-
345
- // 转为 BigNumber 类型
346
- val = new BigNumber(val)
347
-
348
- // 如果为有效数字
349
- if (val.isFinite()) {
350
-
351
- // 如果不为 0
352
- if (! val.isZero()) {
353
-
354
- // 如果为人民币的分转元
355
- if (props.centToYuan && isCentToYuan) {
356
- // 除 100
357
- val = val.dividedBy(100)
358
- }
359
-
360
- // 如果设置了小数位数
361
- if (currentDecimalLength.value) {
362
- // 将值舍入 xx 位精度(如 68.345 -> 68.34)
363
- val = val.dp(currentDecimalLength.value, props.roundMode)
364
-
365
- // 否则值为整数
366
- } else {
367
- // 将值取整
368
- val = val.integerValue(props.roundMode)
369
- }
370
- }
371
-
372
- // 转为数字
373
- return isToNumber ? val.toNumber() : val
374
- }
375
-
376
- return defaultValue
377
- }
378
-
379
- /**
380
- * 格式化为更新值
381
- */
382
- function formatToModelValue(value) {
383
-
384
- // 转为 BigNumber 类型
385
- let val = new BigNumber(value)
386
-
387
- // 如果不是有效数字
388
- if (! val.isFinite()) {
389
-
390
- // 返回当前值
391
- return value
392
- }
393
-
394
- // 如果为人民币的分转元
395
- if (props.centToYuan) {
396
- // 乘以 100
397
- val = val.times(100)
398
- // 再取整(分必须是整数)
399
- .integerValue(props.roundMode)
400
- }
401
-
402
- // 将值转为数字
403
- return val.toNumber()
404
- }
405
-
406
- /**
407
- * 格式化值
408
- */
409
- function formatToCurrentValue(value, isCentToYuan) {
410
-
411
- // 格式化数字
412
- const val = formatNumber(value, isCentToYuan, false, false)
413
-
414
- // 如果为有效数字
415
- if (val !== false) {
416
-
417
- // 如果值 >= 最大值
418
- if (currentMax.value !== null && val.gte(currentMax.value)) {
419
-
420
- // 返回最大值
421
- return currentMax.value
422
- }
423
-
424
- // 如果值 <= 最小值
425
- if (currentMin.value !== null && val.lte(currentMin.value)) {
426
-
427
- // 返回最小值
428
- return currentMin.value
429
- }
430
-
431
- // 将值转为数字
432
- return val.toNumber()
433
- }
434
-
435
- if (
436
- // 如果不允许值为空
437
- props.noEmpty
438
- // 如果有最小值
439
- && currentMin.value !== null
440
- ) {
441
- // 则返回最小值
442
- return currentMin.value
443
- }
444
-
445
- return ''
446
- }
447
-
448
- /**
449
- * 失去焦点触发
450
- */
451
- function onBlur() {
452
-
453
- // 格式化当前值
454
- let val = formatToCurrentValue(currentValue.value, false)
455
-
456
- // 如果当前值有变动
457
- if (val !== currentValue.value) {
458
-
459
- // 更新当前值
460
- currentValue.value = val
461
-
462
- // 将当前值转为声明值
463
- val = formatToModelValue(val)
464
-
465
- // 触发更新值
466
- emitModelValue(val)
467
-
468
- // 失去焦点触发
469
- emit('blur', val)
470
- }
471
- }
472
-
473
- /**
474
- * 改变值
475
- */
476
- function onChange(type) {
477
-
478
- // 格式化当前值
479
- const val = formatToCurrentValue(
480
- new BigNumber(+currentValue.value)
481
- // 增加 / 减少
482
- .plus(type === 'minus' ? -currentStep.value : +currentStep.value)
483
- .toNumber()
484
- , false
485
- )
486
-
487
- // 如果当前值有变动
488
- if (val !== currentValue.value) {
489
-
490
- // 更新当前值
491
- currentValue.value = val
492
-
493
- // 触发更新值
494
- emitModelValue(formatToModelValue(val))
495
- }
496
- }
497
-
498
- // ==========【返回】=============================================================================================
499
-
500
- return {
501
- // 插槽标识
502
- slotNames,
503
- // 当前值
504
- currentValue,
505
- // 当前是否禁用减少按钮
506
- currentDisableMinus,
507
- // 当前是否禁用增加按钮
508
- currentDisablePlus,
509
-
510
- // 失去焦点触发
511
- onBlur,
512
- // 改变值
513
- onChange,
514
- }
515
- }
516
- }
517
- </script>
518
-
519
- <style lang="scss">
520
- .n-input-number {
521
-
522
- // 居中显示
523
- &--center {
524
- &.q-field {
525
- &--outlined {
526
- .q-field__control {
527
- .q-field__native {
528
- text-align: center;
529
- }
530
- }
531
- }
532
- }
533
- }
534
-
535
- // 左边按钮
536
- &__left {
537
- margin-left: -8px;
538
- }
539
-
540
- // 右边按钮
541
- &__right {
542
- margin-right: -8px;
543
- }
544
- }
545
- </style>
546
-
1
+ <template>
2
+ <q-input
3
+ class="n-input-number"
4
+ :class="{
5
+ 'n-input-number--center': center,
6
+ }"
7
+ :disable="disable"
8
+ :readonly="readonly"
9
+ v-model="currentValue"
10
+ @blur="onBlur"
11
+ v-bind="$attrs"
12
+ >
13
+ <!-- 内部前置插槽 -->
14
+ <template #prepend v-if="controls && center">
15
+
16
+ <!-- 减少按钮 -->
17
+ <q-btn
18
+ class="n-input-number__left"
19
+ color="default"
20
+ icon="remove"
21
+ flat
22
+ dense
23
+ :disable="currentDisableMinus"
24
+ @click="onChange('minus')"
25
+ />
26
+
27
+ <!-- 内部前置插槽 -->
28
+ <slot name="prepend" />
29
+
30
+ </template>
31
+
32
+ <!-- 内部后置插槽 -->
33
+ <template #append v-if="controls">
34
+
35
+ <!-- 内部后置插槽 -->
36
+ <slot name="append" />
37
+
38
+ <!-- 减少按钮 -->
39
+ <q-btn
40
+ color="default"
41
+ icon="remove"
42
+ flat
43
+ dense
44
+ :disable="currentDisableMinus"
45
+ @click="onChange('minus')"
46
+ v-if="! center"
47
+ />
48
+
49
+ <!-- 增加按钮 -->
50
+ <q-btn
51
+ class="n-input-number__right"
52
+ color="default"
53
+ icon="add"
54
+ flat
55
+ dense
56
+ :disable="currentDisablePlus"
57
+ @click="onChange('plus')"
58
+ />
59
+
60
+ </template>
61
+
62
+ <!-- 插槽 -->
63
+ <template
64
+ v-for="slotName in slotNames"
65
+ v-slot:[slotName]
66
+ >
67
+ <slot :name="slotName" />
68
+ </template>
69
+
70
+ </q-input>
71
+ </template>
72
+
73
+ <script>
74
+ import { computed, ref, watch } from 'vue'
75
+ import BigNumber from 'bignumber.js'
76
+
77
+ import $n_filter from 'lodash/filter'
78
+
79
+ import $n_isValidObject from '@netang/utils/isValidObject'
80
+
81
+ export default {
82
+
83
+ /**
84
+ * 标识
85
+ */
86
+ name: 'NInputNumber',
87
+
88
+ /**
89
+ * 声明属性
90
+ */
91
+ props: {
92
+ // 值 v-model
93
+ modelValue: {
94
+ required: true,
95
+ },
96
+ // 最小值
97
+ min: {
98
+ type: [Number, String],
99
+ default: 0,
100
+ },
101
+ // 最大值
102
+ max: {
103
+ type: [Number, String],
104
+ default: Infinity,
105
+ },
106
+ // 步长, 每次点击时改变的值(默认为 1, centToYuan开启后默认为 100)
107
+ step: [Number, String],
108
+ // 小数位数(默认为 0, centToYuan开启后默认为 2)
109
+ decimalLength: [Number, String],
110
+ // 是否禁用减少按钮
111
+ disableMinus: Boolean,
112
+ // 是否禁用增加按钮
113
+ disablePlus: Boolean,
114
+ // 是否使用控制按钮
115
+ controls: Boolean,
116
+ // 居中显示
117
+ center: Boolean,
118
+ // 不允许输入的值为空
119
+ noEmpty: Boolean,
120
+ // 是否为人民币的分转元(值为分, 显示为元)
121
+ // 如果为 true, 则 min / max / step 的值默认的单位为人民币的分
122
+ centToYuan: Boolean,
123
+ // 精度舍入模式(默认: 向下取整)
124
+ // 参考文档: https://mikemcl.github.io/bignumber.js/#constructor-properties
125
+ roundMode: {
126
+ type: Number,
127
+ default: BigNumber.ROUND_DOWN,
128
+ },
129
+ // 是否禁用
130
+ disable: Boolean,
131
+ // 是否只读
132
+ readonly: Boolean,
133
+ },
134
+
135
+ /**
136
+ * 声明事件
137
+ */
138
+ emits: [
139
+ 'update:modelValue',
140
+ 'blur',
141
+ 'minus',
142
+ 'plus',
143
+ ],
144
+
145
+ /**
146
+ * 组合式
147
+ */
148
+ setup(props, { emit, slots }) {
149
+
150
+ // ==========【计算属性】=========================================================================================
151
+
152
+ /**
153
+ * 插槽标识
154
+ */
155
+ const slotNames = computed(function() {
156
+
157
+ if ($n_isValidObject(slots)) {
158
+
159
+ // 忽略插槽
160
+ const ignoreKeys = []
161
+
162
+ if (props.controls) {
163
+ ignoreKeys.push('append')
164
+
165
+ if (props.center) {
166
+ ignoreKeys.push('prepend')
167
+ }
168
+ }
169
+
170
+ const keys = Object.keys(slots)
171
+
172
+ if (ignoreKeys.length) {
173
+ return $n_filter(keys, e => ignoreKeys.indexOf(e) === -1)
174
+ }
175
+
176
+ return keys
177
+ }
178
+
179
+ return []
180
+ })
181
+
182
+ /**
183
+ * 当前最小值
184
+ */
185
+ const currentMin = computed(function() {
186
+ // 格式化数字
187
+ return formatNumber(props.min, true, true, null)
188
+ })
189
+
190
+ /**
191
+ * 当前最大值
192
+ */
193
+ const currentMax = computed(function() {
194
+
195
+ // 如果为无限大
196
+ if (props.max === Infinity) {
197
+ // 则返回无限大
198
+ return Infinity
199
+ }
200
+
201
+ // 格式化数字
202
+ return formatNumber(props.max, true, true, null)
203
+ })
204
+
205
+ /**
206
+ * 当前步长(默认为 1, centToYuan开启后默认为 100)
207
+ */
208
+ const currentStep = computed(function() {
209
+
210
+ // 格式化数字
211
+ return formatNumber(props.step ?? (props.centToYuan ? 100 : 1), true, true, null)
212
+ })
213
+
214
+ /**
215
+ * 当前小数位数(默认为 0, centToYuan开启后默认为 2)
216
+ */
217
+ const currentDecimalLength = computed(function() {
218
+ return props.decimalLength ?? (props.centToYuan ? 2 : 0)
219
+ })
220
+
221
+ /**
222
+ * 当前是否禁用减少按钮
223
+ */
224
+ const currentDisableMinus = computed(function () {
225
+
226
+ // 如果禁用 || 如果只读 || 禁用减少按钮
227
+ if (props.disable || props.readonly || props.disableMinus) {
228
+ // 则禁用减少按钮
229
+ return true
230
+ }
231
+
232
+ // 如果没有当前最小值
233
+ if (currentMin.value === null) {
234
+ // 则不禁用减少按钮
235
+ return false
236
+ }
237
+
238
+ // 将当前值转为 BigNumber 类型
239
+ const val = new BigNumber(currentValue.value)
240
+
241
+ // 如果当前值不是有效数字
242
+ if (! val.isFinite()) {
243
+ // 则禁用减少按钮
244
+ return true
245
+ }
246
+
247
+ // 当前值 <= 当前最小值
248
+ return val.lte(currentMin.value)
249
+ })
250
+
251
+ /**
252
+ * 当前是否禁用增加按钮
253
+ */
254
+ const currentDisablePlus = computed(function () {
255
+
256
+ // 如果禁用 || 如果只读 || 禁用增加按钮
257
+ if (props.disable || props.readonly || props.disablePlus) {
258
+ // 则禁用增加按钮
259
+ return true
260
+ }
261
+
262
+ // 如果没有当前最大值
263
+ if (currentMax.value === null) {
264
+ // 则不禁用增加按钮
265
+ return false
266
+ }
267
+
268
+ // 将当前值转为 BigNumber 类型
269
+ const val = new BigNumber(currentValue.value)
270
+
271
+ // 如果当前值不是有效数字
272
+ if (! val.isFinite()) {
273
+ // 则禁用减少按钮
274
+ return true
275
+ }
276
+
277
+ // 当前值 >= 当前最大值
278
+ return val.gte(currentMax.value)
279
+ })
280
+
281
+ // ==========【数据】============================================================================================
282
+
283
+ // 格式化为当前值
284
+ const currentValue = ref(formatToCurrentValue(props.modelValue, true))
285
+
286
+ // 如果当前值 !== 声明值
287
+ const rawModelValue = formatToModelValue(currentValue.value)
288
+ if (rawModelValue !== props.modelValue) {
289
+
290
+ // 触发更新值
291
+ emitModelValue(rawModelValue)
292
+ }
293
+
294
+ // ==========【监听数据】=========================================================================================
295
+
296
+ /**
297
+ * 监听声明值
298
+ */
299
+ watch(() => props.modelValue, function (val) {
300
+
301
+ // 格式化为当前值
302
+ val = formatToCurrentValue(val, true)
303
+
304
+ // 如果当前值有变化
305
+ if (val !== currentValue.value) {
306
+ currentValue.value = val
307
+ }
308
+ })
309
+
310
+ /**
311
+ * 监听 当前最小值 / 当前最大值 / 当前小数位数
312
+ */
313
+ watch([currentMin, currentMax, currentDecimalLength], function () {
314
+
315
+ // 格式化当前值
316
+ const val = formatToCurrentValue(currentValue.value, false)
317
+
318
+ // 如果当前值有变化
319
+ if (val !== currentValue.value) {
320
+
321
+ // 更新当前值
322
+ currentValue.value = val
323
+
324
+ // 更新值
325
+ emitModelValue(formatToModelValue(val))
326
+ }
327
+ })
328
+
329
+ // ==========【方法】=============================================================================================
330
+
331
+ /**
332
+ * 触发更新值
333
+ */
334
+ function emitModelValue(val) {
335
+
336
+ // 触发更新值
337
+ emit('update:modelValue', val)
338
+ }
339
+
340
+ /**
341
+ * 格式化数字
342
+ */
343
+ function formatNumber(val, isCentToYuan, isToNumber, defaultValue) {
344
+
345
+ // 转为 BigNumber 类型
346
+ val = new BigNumber(val)
347
+
348
+ // 如果为有效数字
349
+ if (val.isFinite()) {
350
+
351
+ // 如果不为 0
352
+ if (! val.isZero()) {
353
+
354
+ // 如果为人民币的分转元
355
+ if (props.centToYuan && isCentToYuan) {
356
+ // 除 100
357
+ val = val.dividedBy(100)
358
+ }
359
+
360
+ // 如果设置了小数位数
361
+ if (currentDecimalLength.value) {
362
+ // 将值舍入 xx 位精度(如 68.345 -> 68.34)
363
+ val = val.dp(currentDecimalLength.value, props.roundMode)
364
+
365
+ // 否则值为整数
366
+ } else {
367
+ // 将值取整
368
+ val = val.integerValue(props.roundMode)
369
+ }
370
+ }
371
+
372
+ // 转为数字
373
+ return isToNumber ? val.toNumber() : val
374
+ }
375
+
376
+ return defaultValue
377
+ }
378
+
379
+ /**
380
+ * 格式化为更新值
381
+ */
382
+ function formatToModelValue(value) {
383
+
384
+ // 转为 BigNumber 类型
385
+ let val = new BigNumber(value)
386
+
387
+ // 如果不是有效数字
388
+ if (! val.isFinite()) {
389
+
390
+ // 返回当前值
391
+ return value
392
+ }
393
+
394
+ // 如果为人民币的分转元
395
+ if (props.centToYuan) {
396
+ // 乘以 100
397
+ val = val.times(100)
398
+ // 再取整(分必须是整数)
399
+ .integerValue(props.roundMode)
400
+ }
401
+
402
+ // 将值转为数字
403
+ return val.toNumber()
404
+ }
405
+
406
+ /**
407
+ * 格式化值
408
+ */
409
+ function formatToCurrentValue(value, isCentToYuan) {
410
+
411
+ // 格式化数字
412
+ const val = formatNumber(value, isCentToYuan, false, false)
413
+
414
+ // 如果为有效数字
415
+ if (val !== false) {
416
+
417
+ // 如果值 >= 最大值
418
+ if (currentMax.value !== null && val.gte(currentMax.value)) {
419
+
420
+ // 返回最大值
421
+ return currentMax.value
422
+ }
423
+
424
+ // 如果值 <= 最小值
425
+ if (currentMin.value !== null && val.lte(currentMin.value)) {
426
+
427
+ // 返回最小值
428
+ return currentMin.value
429
+ }
430
+
431
+ // 将值转为数字
432
+ return val.toNumber()
433
+ }
434
+
435
+ if (
436
+ // 如果不允许值为空
437
+ props.noEmpty
438
+ // 如果有最小值
439
+ && currentMin.value !== null
440
+ ) {
441
+ // 则返回最小值
442
+ return currentMin.value
443
+ }
444
+
445
+ return ''
446
+ }
447
+
448
+ /**
449
+ * 失去焦点触发
450
+ */
451
+ function onBlur() {
452
+
453
+ // 格式化当前值
454
+ let val = formatToCurrentValue(currentValue.value, false)
455
+
456
+ // 如果当前值有变动
457
+ if (val !== currentValue.value) {
458
+
459
+ // 更新当前值
460
+ currentValue.value = val
461
+
462
+ // 将当前值转为声明值
463
+ val = formatToModelValue(val)
464
+
465
+ // 触发更新值
466
+ emitModelValue(val)
467
+
468
+ // 失去焦点触发
469
+ emit('blur', val)
470
+ }
471
+ }
472
+
473
+ /**
474
+ * 改变值
475
+ */
476
+ function onChange(type) {
477
+
478
+ // 格式化当前值
479
+ const val = formatToCurrentValue(
480
+ new BigNumber(+currentValue.value)
481
+ // 增加 / 减少
482
+ .plus(type === 'minus' ? -currentStep.value : +currentStep.value)
483
+ .toNumber()
484
+ , false
485
+ )
486
+
487
+ // 如果当前值有变动
488
+ if (val !== currentValue.value) {
489
+
490
+ // 更新当前值
491
+ currentValue.value = val
492
+
493
+ // 触发更新值
494
+ emitModelValue(formatToModelValue(val))
495
+ }
496
+ }
497
+
498
+ // ==========【返回】=============================================================================================
499
+
500
+ return {
501
+ // 插槽标识
502
+ slotNames,
503
+ // 当前值
504
+ currentValue,
505
+ // 当前是否禁用减少按钮
506
+ currentDisableMinus,
507
+ // 当前是否禁用增加按钮
508
+ currentDisablePlus,
509
+
510
+ // 失去焦点触发
511
+ onBlur,
512
+ // 改变值
513
+ onChange,
514
+ }
515
+ }
516
+ }
517
+ </script>
518
+
519
+ <style lang="scss">
520
+ .n-input-number {
521
+
522
+ // 居中显示
523
+ &--center {
524
+ &.q-field {
525
+ &--outlined {
526
+ .q-field__control {
527
+ .q-field__native {
528
+ text-align: center;
529
+ }
530
+ }
531
+ }
532
+ }
533
+ }
534
+
535
+ // 左边按钮
536
+ &__left {
537
+ margin-left: -8px;
538
+ }
539
+
540
+ // 右边按钮
541
+ &__right {
542
+ margin-right: -8px;
543
+ }
544
+ }
545
+ </style>
546
+