@ecolovo/uniapp-components 1.0.0

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 (97) hide show
  1. package/components/aspect/aspect.vue +43 -0
  2. package/components/badge/badge.vue +88 -0
  3. package/components/button/button.vue +409 -0
  4. package/components/checkbox/checkbox.vue +183 -0
  5. package/components/checkbox-group/checkbox-group.vue +101 -0
  6. package/components/checkbox-group2/checkbox-group2.vue +138 -0
  7. package/components/checkbox-group2/checkbox.vue +103 -0
  8. package/components/data-list/data-list.vue +130 -0
  9. package/components/date-picker/date-picker-view.vue +181 -0
  10. package/components/date-picker/date-picker.vue +237 -0
  11. package/components/datetime-picker/datetime-picker-view.vue +209 -0
  12. package/components/datetime-picker/datetime-picker.vue +235 -0
  13. package/components/dialog/dialog-content.vue +162 -0
  14. package/components/dialog/dialog.vue +166 -0
  15. package/components/dropdown/dropdown.vue +145 -0
  16. package/components/empty/empty.vue +102 -0
  17. package/components/flex/flex.vue +76 -0
  18. package/components/form/form.vue +43 -0
  19. package/components/form/mixin.js +13 -0
  20. package/components/form-item/form-item.vue +141 -0
  21. package/components/icon/icon.vue +114 -0
  22. package/components/image/image.vue +61 -0
  23. package/components/input/input.vue +154 -0
  24. package/components/input/password-input.vue +95 -0
  25. package/components/list-page/list-page.js +10 -0
  26. package/components/list-page/list-page.vue +150 -0
  27. package/components/load-more/load-more.vue +97 -0
  28. package/components/loading/animations/loading-animation1.vue +92 -0
  29. package/components/loading/animations/loading-animation2.vue +28 -0
  30. package/components/loading/animations/loading-animation3.vue +81 -0
  31. package/components/loading/animations/loading-animation4.vue +74 -0
  32. package/components/loading/loading.vue +91 -0
  33. package/components/message-box/message-box.vue +60 -0
  34. package/components/number-dialog/number-dialog.vue +360 -0
  35. package/components/number-input/number-input.vue +352 -0
  36. package/components/numpad/numpad.vue +224 -0
  37. package/components/page/page-body.vue +227 -0
  38. package/components/page/page-footer.vue +75 -0
  39. package/components/page/page-header.vue +222 -0
  40. package/components/page/page.vue +128 -0
  41. package/components/picker/picker-popup.vue +295 -0
  42. package/components/picker/picker.vue +151 -0
  43. package/components/popover/popover-content.vue +255 -0
  44. package/components/popover/popover-mask.vue +113 -0
  45. package/components/popover/popover.vue +84 -0
  46. package/components/popup/popup-content.vue +162 -0
  47. package/components/popup/popup.vue +168 -0
  48. package/components/price/price.vue +104 -0
  49. package/components/search/search.vue +220 -0
  50. package/components/searching/searching.vue +86 -0
  51. package/components/selector/selector.vue +86 -0
  52. package/components/switch/switch.vue +180 -0
  53. package/components/tab/tab.vue +235 -0
  54. package/components/table/table.vue +112 -0
  55. package/components/textarea/textarea.vue +192 -0
  56. package/components/time-picker/time-picker-view.vue +127 -0
  57. package/components/time-picker/time-picker.vue +140 -0
  58. package/components/uploader/download.js +81 -0
  59. package/components/uploader/file-uploader.vue +52 -0
  60. package/components/uploader/files.js +225 -0
  61. package/components/uploader/media-uploader.vue +133 -0
  62. package/components/uploader/mixin.js +106 -0
  63. package/components/uploader/uni.js +38 -0
  64. package/components/uploader/upload-button.vue +29 -0
  65. package/components/uploader/uploader copy.vue +547 -0
  66. package/components/uploader/uploader.vue +117 -0
  67. package/configs/index.js +41 -0
  68. package/easyCom.js +25 -0
  69. package/init.js +17 -0
  70. package/mixins/componentName.js +32 -0
  71. package/mixins/listPage.js +1 -0
  72. package/mixins/windowInfo.js +1 -0
  73. package/package.json +12 -0
  74. package/resources/iconfont/uniapp_ui_icon/demo.css +539 -0
  75. package/resources/iconfont/uniapp_ui_icon/demo_index.html +555 -0
  76. package/resources/iconfont/uniapp_ui_icon/iconfont.css +78 -0
  77. package/resources/iconfont/uniapp_ui_icon/iconfont.js +1 -0
  78. package/resources/iconfont/uniapp_ui_icon/iconfont.json +121 -0
  79. package/resources/iconfont/uniapp_ui_icon/iconfont.woff2 +0 -0
  80. package/resources/image/empty.svg +13 -0
  81. package/styles/global.scss +24 -0
  82. package/styles/index.scss +2 -0
  83. package/styles/utils/css-var.scss +87 -0
  84. package/styles/utils/ellipsis.scss +26 -0
  85. package/styles/utils/global-var.scss +11 -0
  86. package/styles/utils/index.scss +5 -0
  87. package/styles/utils/selector.scss +5 -0
  88. package/styles/utils/z-index.scss +10 -0
  89. package/styles/var/component.scss +242 -0
  90. package/styles/var/index.scss +2 -0
  91. package/styles/var/theme.scss +40 -0
  92. package/utils/date.js +151 -0
  93. package/utils/floor.js +20 -0
  94. package/utils/freezeDeep.js +19 -0
  95. package/utils/price.js +32 -0
  96. package/utils/round.js +22 -0
  97. package/utils/windowInfo.js +77 -0
@@ -0,0 +1,360 @@
1
+ <template>
2
+ <view
3
+ :class="[componentClassNames, { [$style['NumberDialog--disabled']]: disabled }]"
4
+ @click.stop.prevent="doNothing"
5
+ >
6
+ <view
7
+ v-if="!isReadonly"
8
+ :class="[
9
+ $style['NumberDialog--step-button'],
10
+ $style['NumberDialog--step-button-minus'],
11
+ { [$style['NumberDialog--step-button-disabled']]: disabledDecrease },
12
+ ]"
13
+ >
14
+ <icon-cpn
15
+ icon="step-minus"
16
+ tap-size="60rpx"
17
+ @touchstart.native="handleStepLongTouchStart($event, -step, disabledDecrease)"
18
+ @touchend.native="handleStepLongTouchEnd(-step)"
19
+ @touchcancel.native="handleStepLongTouchEnd(-step)"
20
+ />
21
+ </view>
22
+ <view
23
+ :class="[$style['NumberDialog--input'], { [$style['NumberDialog--readonly']]: isReadonly }]"
24
+ @click="showPad"
25
+ >
26
+ <text v-if="isReadonly || value === 0 || value" :class="$style['NumberDialog--value']">{{ value }}</text>
27
+ <text v-else :class="$style['NumberDialog--placeholder']">{{ placeholder }}</text>
28
+ </view>
29
+ <view
30
+ v-if="!isReadonly"
31
+ :class="[
32
+ $style['NumberDialog--step-button'],
33
+ $style['NumberDialog--step-button-plus'],
34
+ { [$style['NumberDialog--step-button-disabled']]: disabledIncrease },
35
+ ]"
36
+ >
37
+ <icon-cpn
38
+ icon="step-plus"
39
+ tap-size="60rpx"
40
+ @touchstart.native="handleStepLongTouchStart($event, step, disabledIncrease)"
41
+ @touchend.native="handleStepLongTouchEnd(step)"
42
+ @touchcancel.native="handleStepLongTouchEnd(step)"
43
+ />
44
+ </view>
45
+
46
+ <popover-mask-cpn v-if="isShowPad" ref="mask" v-model="isShowPad">
47
+ <dialog-content-cpn
48
+ title="请输入数字"
49
+ center-title
50
+ position="center"
51
+ :class="$style['NumberDialog--dialog']"
52
+ @confirm="onConfirm()"
53
+ @cancel="closePad()"
54
+ >
55
+ <view :class="$style['NumberDialog--dialog-input']">
56
+ <text v-if="editValue === 0 || editValue">{{ editValue }}</text>
57
+ <text v-else :class="$style['NumberDialog--placeholder']">{{ placeholder }}</text>
58
+ </view>
59
+ </dialog-content-cpn>
60
+ <popover-content-cpn position="bottom">
61
+ <numpad-cpn v-model="editValue" :precision="precision" :min="min" :max="max" />
62
+ </popover-content-cpn>
63
+ </popover-mask-cpn>
64
+ </view>
65
+ </template>
66
+
67
+ <script>
68
+ import componentName from '../../mixins/componentName';
69
+ import { isFormReadonly } from '../form/mixin';
70
+ import { round } from '../../utils/round';
71
+ import DialogContentCpn from '../dialog/dialog-content.vue';
72
+ import IconCpn from '../icon/icon.vue';
73
+ import NumpadCpn from '../numpad/numpad.vue';
74
+ import PopoverContentCpn from '../popover/popover-content.vue';
75
+ import PopoverMaskCpn from '../popover/popover-mask.vue';
76
+
77
+ const MIN = 0;
78
+ const MAX = 9999;
79
+
80
+ export default {
81
+ name: 'AppNumberDialog',
82
+ options: {
83
+ virtualHost: true,
84
+ styleIsolation: 'shared',
85
+ },
86
+ components: { DialogContentCpn, IconCpn, NumpadCpn, PopoverContentCpn, PopoverMaskCpn },
87
+ mixins: [componentName, isFormReadonly],
88
+ props: {
89
+ /** 绑定值 */
90
+ value: {
91
+ type: [Number, String],
92
+ default: '',
93
+ },
94
+ /** 是否禁用 */
95
+ disabled: {
96
+ type: Boolean,
97
+ default: false,
98
+ },
99
+ /** 是否可编辑,为 true 时点击中间数字会弹出键盘 */
100
+ editable: {
101
+ type: Boolean,
102
+ default: true,
103
+ },
104
+ /** 是否只读 */
105
+ readonly: {
106
+ type: Boolean,
107
+ default: false,
108
+ },
109
+ /** 占位提示文本 */
110
+ placeholder: {
111
+ type: String,
112
+ default: '请输入',
113
+ },
114
+ /** 小数的精度 */
115
+ precision: {
116
+ type: Number,
117
+ default: 0,
118
+ },
119
+ /** 步进器的步长 */
120
+ step: {
121
+ type: Number,
122
+ default: 1,
123
+ },
124
+ /** 最大值 */
125
+ max: {
126
+ type: Number,
127
+ default: MAX,
128
+ },
129
+ /** 最小值 */
130
+ min: {
131
+ type: Number,
132
+ default: MIN,
133
+ },
134
+ },
135
+ data() {
136
+ return {
137
+ /** 编辑值 */
138
+ editValue: '',
139
+ /** 是否显示数字键盘 */
140
+ isShowPad: false,
141
+ };
142
+ },
143
+ computed: {
144
+ /** 是否只读 */
145
+ isReadonly() {
146
+ return this.readonly || this.isFormReadonly;
147
+ },
148
+ /** 是否禁用减 */
149
+ disabledDecrease() {
150
+ if (this.disabled) return true;
151
+ if (!isNaN(this.value) && +this.value <= Number(this.min)) return true;
152
+ return false;
153
+ },
154
+ /** 是否禁用增 */
155
+ disabledIncrease() {
156
+ if (this.disabled) return true;
157
+ if (!isNaN(this.value) && +this.value >= Number(this.max)) return true;
158
+ return false;
159
+ },
160
+ },
161
+ watch: {
162
+ value: {
163
+ handler(n) {
164
+ this.editValue = n + '';
165
+ },
166
+ immediate: true,
167
+ },
168
+ },
169
+ methods: {
170
+ doNothing() {},
171
+ /** 长按步进按钮开始处理 */
172
+ handleStepLongTouchStart(event, step, disabled) {
173
+ if (disabled) return;
174
+ if (event.touches && event.touches.length > 1) return;
175
+
176
+ this._lastValue = this.value;
177
+ this._touchingStep = step;
178
+ let interval = 400;
179
+ let times = 0;
180
+
181
+ const longTouch = () => {
182
+ if (!this.handleOneStep(step * (times >= 30 ? 4 : times >= 12 ? 2 : 1))) return;
183
+
184
+ this._touchTimer = setTimeout(longTouch, interval);
185
+ interval = Math.max(100, interval - 50);
186
+ times++;
187
+ };
188
+ longTouch();
189
+ },
190
+ /** 长按步进按钮结束处理 */
191
+ handleStepLongTouchEnd(step) {
192
+ if (this._touchingStep !== step) return;
193
+ if (this._touchTimer) {
194
+ clearTimeout(this._touchTimer);
195
+ this._touchTimer = null;
196
+ }
197
+ this._touchingStep = null;
198
+ this.confirmValue(this.editValue + '', true);
199
+ },
200
+ /** 单次步进处理 */
201
+ handleOneStep(step) {
202
+ if ((step > 0 && this.disabledIncrease) || (step < 0 && this.disabledDecrease)) {
203
+ return false;
204
+ }
205
+ let nextValue = round(+this.value + step, this.precision);
206
+
207
+ this.updateStepValue(nextValue + '');
208
+ return true;
209
+ },
210
+ /** 每次步进处理 */
211
+ updateStepValue(value) {
212
+ value = this.validateValue(value, true);
213
+ this.$emit('input', value);
214
+ this.editValue = value;
215
+ },
216
+ /** 校验数据
217
+ * @param {string} value 输入值
218
+ * @param {boolean} isStepButton 是否为步进按钮触发
219
+ */
220
+ validateValue(value, isStepButton = false) {
221
+ let nextValue;
222
+ if (isNaN(value)) {
223
+ // 输入不合法,取消此次输入
224
+ nextValue = isStepButton ? 0 : this._lastValue;
225
+ this.$emit('error', '请输入正确的数值');
226
+ } else if (value === '') {
227
+ // 输入空值
228
+ nextValue = isStepButton ? 0 : '';
229
+ this.$emit('error', '请输入数值');
230
+ } else if (+value < this.min) {
231
+ // 超过最小输入值
232
+ nextValue = this.min;
233
+ this.$emit('error', `数值不能小于 ${this.min}`);
234
+ } else if (+value > this.max) {
235
+ // 超过最大输入值
236
+ nextValue = this.max;
237
+ this.$emit('error', `数值不能大于 ${this.max}`);
238
+ } else {
239
+ nextValue = round(+value, this.precision);
240
+ }
241
+ return nextValue;
242
+ },
243
+ /** 确认数据
244
+ * @param {string} value
245
+ */
246
+ confirmValue(value, isStepButton = false) {
247
+ value = this.validateValue(value, isStepButton);
248
+ this.editValue = value + '';
249
+
250
+ this.$emit('input', value);
251
+ this.$emit('change', { value, lastValue: this._lastValue });
252
+ },
253
+ /** 显示数字键盘弹框 */
254
+ showPad() {
255
+ if (this.isReadonly || this.disabled || !this.editable) return;
256
+
257
+ this.editValue = this.value + '';
258
+ this.isShowPad = true;
259
+ },
260
+ /** 关闭数字键盘弹框 */
261
+ closePad() {
262
+ this.$refs.mask.close();
263
+ },
264
+ /** 点击弹框确认按钮 */
265
+ onConfirm() {
266
+ this.confirmValue(this.editValue);
267
+ this.closePad();
268
+ },
269
+ },
270
+ };
271
+ </script>
272
+
273
+ <style lang="scss" module>
274
+ @use '../../styles/index' as *;
275
+
276
+ // 定义外部变量
277
+ $component-name: 'number-dialog';
278
+ $component-vars: $number-dialog;
279
+ %external-vars {
280
+ @include UTILS__DEFINE_COMPONENT_VARS();
281
+ }
282
+
283
+ .NumberDialog {
284
+ @extend %external-vars;
285
+ position: relative;
286
+ width: 100%;
287
+ height: 100%;
288
+ display: flex;
289
+ justify-content: flex-end;
290
+ align-items: center;
291
+ color: UTILS__GLOBAL_VAR(color, foreground);
292
+
293
+ &::after {
294
+ position: absolute;
295
+ content: '';
296
+ inset: 0;
297
+ background: #ffffff80;
298
+ visibility: hidden;
299
+ opacity: 0;
300
+ transition: all 0.2s;
301
+ }
302
+ &.NumberDialog--disabled::after {
303
+ visibility: visible;
304
+ opacity: 1;
305
+ }
306
+ }
307
+
308
+ .NumberDialog--input {
309
+ width: 4em;
310
+ height: 48rpx;
311
+ line-height: 48rpx;
312
+ text-align: center;
313
+ overflow: hidden;
314
+ &.NumberDialog--readonly {
315
+ width: 100%;
316
+ height: 100%;
317
+ text-align: inherit;
318
+ }
319
+ }
320
+ .NumberDialog--value {
321
+ color: UTILS__GLOBAL_VAR(color, foreground);
322
+ }
323
+ .NumberDialog--placeholder {
324
+ color: UTILS__GLOBAL_VAR(color, placeholder);
325
+ }
326
+
327
+ .NumberDialog--step-button {
328
+ position: relative;
329
+ width: 48rpx;
330
+ height: 48rpx;
331
+ border-radius: 8rpx;
332
+ color: #999;
333
+ background: UTILS__GLOBAL_VAR(color, background);
334
+ font-size: 32rpx;
335
+ display: flex;
336
+ justify-content: center;
337
+ align-items: center;
338
+
339
+ &.NumberDialog--step-button-minus {
340
+ margin-right: 20rpx;
341
+ }
342
+ &.NumberDialog--step-button-plus {
343
+ margin-left: 20rpx;
344
+ }
345
+
346
+ &.NumberDialog--step-button-disabled {
347
+ color: #ccc;
348
+ }
349
+ }
350
+
351
+ .NumberDialog--dialog {
352
+ #{UTILS__VAR_NAME(position, popover)}: calc(50% - 240rpx) auto auto 50%;
353
+ }
354
+ .NumberDialog--dialog-input {
355
+ height: 50rpx;
356
+ line-height: 50rpx;
357
+ font-size: 36rpx;
358
+ text-align: center;
359
+ }
360
+ </style>
@@ -0,0 +1,352 @@
1
+ <template>
2
+ <view
3
+ :class="[componentClassNames, { [$style['NumberInput--step-mode']]: mode === 'step' }]"
4
+ @click.stop.prevent="doNothing"
5
+ >
6
+ <view
7
+ v-if="mode === 'step' && !isReadonly"
8
+ :class="[
9
+ $style['NumberInput--step-button'],
10
+ $style['NumberInput--step-button-minus'],
11
+ { [$style['NumberInput--step-button-disabled']]: disabledDecrease },
12
+ ]"
13
+ >
14
+ <icon-cpn
15
+ icon="step-minus"
16
+ @touchstart.native="handleLongTouchStart($event, -step, disabledDecrease)"
17
+ @touchend.native="handleLongTouchEnd(-step)"
18
+ @touchcancel.native="handleLongTouchEnd(-step)"
19
+ />
20
+ </view>
21
+ <view v-if="isReadonly" :class="[$style['NumberInput--value'], { disabled }]">
22
+ {{ value }}
23
+ </view>
24
+ <input
25
+ v-else-if="editable || mode !== 'step'"
26
+ v-model="editValue"
27
+ :type="type"
28
+ maxlength="14"
29
+ :class="[$style['NumberInput--input'], mode === 'step' ? $style['NumberInput--step-input'] : '']"
30
+ :disabled="disabled"
31
+ :placeholder="placeholder"
32
+ :placeholder-class="$style['NumberInput--input-placeholder']"
33
+ @input="handleInput"
34
+ @focus="handleFocus"
35
+ @blur="handleInputConfirm"
36
+ />
37
+ <view v-else :class="$style['NumberInput--input-value']">{{ value || 0 }}</view>
38
+ <view
39
+ v-if="mode === 'step' && !isReadonly"
40
+ :class="[
41
+ $style['NumberInput--step-button'],
42
+ $style['NumberInput--step-button-plus'],
43
+ { [$style['NumberInput--step-button-disabled']]: disabledIncrease },
44
+ ]"
45
+ >
46
+ <icon-cpn
47
+ icon="step-plus"
48
+ @touchstart.native="handleLongTouchStart($event, step, disabledIncrease)"
49
+ @touchend.native="handleLongTouchEnd(step)"
50
+ @touchcancel.native="handleLongTouchEnd(step)"
51
+ />
52
+ </view>
53
+ </view>
54
+ </template>
55
+
56
+ <script>
57
+ import componentName from '../../mixins/componentName';
58
+ import { isFormReadonly } from '../form/mixin';
59
+ import { round } from '../../utils/round';
60
+ import IconCpn from '../icon/icon.vue';
61
+
62
+ const MIN = 0;
63
+ const MAX = 9999999999;
64
+ const numberReg = /^(-?)(\d*)()(-?)$/;
65
+
66
+ export default {
67
+ name: 'AppNumberInput',
68
+ options: {
69
+ virtualHost: true,
70
+ styleIsolation: 'shared',
71
+ },
72
+ components: { IconCpn },
73
+ mixins: [componentName, isFormReadonly],
74
+ props: {
75
+ /** 绑定值 */
76
+ value: {
77
+ type: [Number, String],
78
+ default: '',
79
+ },
80
+ /** 是否禁用 */
81
+ disabled: {
82
+ type: Boolean,
83
+ default: false,
84
+ },
85
+ /** 是否可编辑 */
86
+ editable: {
87
+ type: Boolean,
88
+ default: true,
89
+ },
90
+ /** 是否只读 */
91
+ readonly: {
92
+ type: Boolean,
93
+ default: false,
94
+ },
95
+ /** 类型: input: 只有输入框 | step: 步进器,即带 + - 按钮 */
96
+ mode: {
97
+ type: String,
98
+ validator: (val) => !val || ['input', 'step'].includes(val),
99
+ default: 'input',
100
+ },
101
+ /** 输入模式: number: 只能输入整数 | digit: 小数 | text: 如需输入负数 */
102
+ type: {
103
+ type: String,
104
+ validator: (val) => !val || ['number', 'digit', 'text'].includes(val),
105
+ default: 'number',
106
+ },
107
+ /** 占位提示文本 */
108
+ placeholder: {
109
+ type: String,
110
+ default: '请输入',
111
+ },
112
+ /** 小数的精度 */
113
+ precision: {
114
+ type: Number,
115
+ default: 0,
116
+ },
117
+ /** 步进器的步长 */
118
+ step: {
119
+ type: Number,
120
+ default: 1,
121
+ },
122
+ /** 最大值 */
123
+ max: {
124
+ type: Number,
125
+ default: MAX,
126
+ },
127
+ /** 最小值 */
128
+ min: {
129
+ type: Number,
130
+ default: MIN,
131
+ },
132
+ },
133
+ data() {
134
+ return {
135
+ /** 显示值 */
136
+ editValue: '',
137
+ };
138
+ },
139
+ computed: {
140
+ isReadonly() {
141
+ return this.readonly || this.isFormReadonly;
142
+ },
143
+ inputTestReg() {
144
+ return this.precision > 0 ? new RegExp('^(-?)(\\d*)(\\.\\d{0,' + this.precision + '})?(-?)$') : numberReg;
145
+ },
146
+ /** 是否禁用减 */
147
+ disabledDecrease() {
148
+ if (this.disabled) return true;
149
+ const val = Number(this.value);
150
+ if (!Number.isNaN(val) && val <= Number(this.min)) return true;
151
+ return false;
152
+ },
153
+ /** 是否禁用增 */
154
+ disabledIncrease() {
155
+ if (this.disabled) return true;
156
+ const val = Number(this.value);
157
+ if (!Number.isNaN(val) && val >= Number(this.max)) return true;
158
+ return false;
159
+ },
160
+ },
161
+ watch: {
162
+ value: {
163
+ handler(n) {
164
+ this.editValue = n + '';
165
+ },
166
+ immediate: true,
167
+ },
168
+ },
169
+ methods: {
170
+ doNothing() {},
171
+ /** 聚焦 */
172
+ handleFocus() {
173
+ this._lastValue = this.value;
174
+ this._lastEditValue = this.value;
175
+ },
176
+ handleLongTouchStart(event, step, disabled) {
177
+ if (disabled) return;
178
+ if (event.touches && event.touches.length > 1) return;
179
+
180
+ this._lastValue = this.value;
181
+ this._touchStep = step;
182
+ let interval = 400;
183
+ let times = 0;
184
+
185
+ const longTouch = () => {
186
+ if (!this.handleStep(step * (times >= 30 ? 4 : times >= 12 ? 2 : 1))) return;
187
+
188
+ this._touchTimer = setTimeout(longTouch, interval);
189
+ interval = Math.max(100, interval - 50);
190
+ times++;
191
+ };
192
+ longTouch();
193
+ },
194
+ handleLongTouchEnd(step) {
195
+ if (this._touchStep !== step) return;
196
+ if (this._touchTimer) {
197
+ clearTimeout(this._touchTimer);
198
+ this._touchTimer = null;
199
+ }
200
+ this._touchStep = null;
201
+ this.confirmValue(this.editValue + '', true);
202
+ },
203
+ /** 点击按钮修改数量 */
204
+ handleStep(step) {
205
+ if ((step > 0 && this.disabledIncrease) || (step < 0 && this.disabledDecrease)) {
206
+ return false;
207
+ }
208
+ let nextValue = round(+this.value + step, this.precision);
209
+
210
+ this.updateStepValue(nextValue + '');
211
+ return true;
212
+ },
213
+ handleInput(e) {
214
+ let value = e.detail.value.trim();
215
+
216
+ const match = this.inputTestReg.exec(value);
217
+ if (match) {
218
+ // sign: 负号; integer: 整数部分数字; decimal: 小数部分数字; resign: 再次输入负号,取消负号
219
+ let [, sign = '', integer = '', decimal = '', resign = ''] = match;
220
+ sign = !!sign ^ !!resign ? '-' : '';
221
+ integer = integer ? Number(integer) || '0' : decimal ? '0' : '';
222
+ value = sign + integer + decimal;
223
+
224
+ this._lastEditValue = value;
225
+ this.$emit('input', value);
226
+ this.$nextTick(() => {
227
+ this.editValue = value;
228
+ });
229
+ } else {
230
+ this.$nextTick(() => {
231
+ this.editValue = this._lastEditValue;
232
+ });
233
+ }
234
+ },
235
+ updateStepValue(value) {
236
+ value = this.validateValue(value, true);
237
+ this.$emit('input', value);
238
+ this.editValue = value;
239
+ },
240
+ /** 修改数目输入完毕 */
241
+ handleInputConfirm(e) {
242
+ let value = e.detail.value;
243
+ this.confirmValue(value);
244
+ },
245
+ /** 确认数据
246
+ * @param {string} value
247
+ */
248
+ confirmValue(value, isStepButton = false) {
249
+ value = this.validateValue(value, isStepButton);
250
+ this.editValue = value + '';
251
+
252
+ this.$emit('input', value);
253
+ this.$emit('change', value);
254
+ },
255
+ /** 确认数据前校验
256
+ * @param {string} value
257
+ */
258
+ validateValue(value, isStepButton = false) {
259
+ let _nextValue;
260
+ if (isNaN(value)) {
261
+ // 输入不合法,取消此次输入
262
+ _nextValue = isStepButton ? 0 : this._lastValue;
263
+ this.$emit('error', '请输入正确的数值');
264
+ } else if (value === '') {
265
+ // 输入空值
266
+ _nextValue = isStepButton ? 0 : '';
267
+ this.$emit('error', '请输入数值');
268
+ } else if (+value < this.min) {
269
+ // 超过最小输入值
270
+ _nextValue = this.min;
271
+ this.$emit('error', `数值不能小于 ${this.min}`);
272
+ } else if (+value > this.max) {
273
+ // 超过最大输入值
274
+ _nextValue = this.max;
275
+ this.$emit('error', `数值不能大于 ${this.max}`);
276
+ } else {
277
+ _nextValue = round(+value, this.precision);
278
+ }
279
+ return _nextValue;
280
+ },
281
+ },
282
+ };
283
+ </script>
284
+
285
+ <style lang="scss" module>
286
+ @use '../../styles/index' as *;
287
+
288
+ // 定义外部变量
289
+ $component-name: 'number-input';
290
+ $component-vars: $number-input;
291
+ %external-vars {
292
+ @include UTILS__DEFINE_COMPONENT_VARS();
293
+ }
294
+
295
+ .NumberInput {
296
+ @extend %external-vars;
297
+ position: relative;
298
+ width: 100%;
299
+ height: 100%;
300
+ display: flex;
301
+ align-items: center;
302
+ color: UTILS__GLOBAL_VAR(color, foreground);
303
+ font-size: UTILS__LOCAL_VAR(font-size);
304
+ }
305
+
306
+ .NumberInput--input {
307
+ flex: 1;
308
+ height: 100%;
309
+ text-align: inherit;
310
+ border-bottom-width: 1rpx;
311
+ .NumberInput--step-mode & {
312
+ text-align: UTILS__LOCAL_VAR(text-align);
313
+ }
314
+ }
315
+ .NumberInput--input-placeholder {
316
+ color: UTILS__GLOBAL_VAR(color, placeholder);
317
+ }
318
+ .NumberInput--input-value {
319
+ @extend .NumberInput--input;
320
+ min-width: 2em;
321
+ height: auto;
322
+ }
323
+ .NumberInput--value {
324
+ text-align: center;
325
+ }
326
+ .NumberInput--step-button {
327
+ position: relative;
328
+ width: 48rpx;
329
+ height: 48rpx;
330
+ border-radius: 8rpx;
331
+ color: #999;
332
+ background: UTILS__GLOBAL_VAR(color, background);
333
+ font-size: 32rpx;
334
+ display: flex;
335
+ justify-content: center;
336
+ align-items: center;
337
+
338
+ &.NumberInput--step-button-minus {
339
+ margin-right: 20rpx;
340
+ }
341
+ &.NumberInput--step-button-plus {
342
+ margin-left: 20rpx;
343
+ }
344
+
345
+ &.NumberInput--step-button-disabled {
346
+ color: #ccc;
347
+ }
348
+ }
349
+ .NumberInput--step-input {
350
+ border: none;
351
+ }
352
+ </style>