hy-app 0.5.12 → 0.5.14
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.
- package/components/hy-button/index.scss +3 -0
- package/components/hy-cell-item/hy-cell-item.vue +161 -161
- package/components/hy-cell-item/props.ts +59 -66
- package/components/hy-config-provider/index.scss +5 -8
- package/components/hy-coupon/index.scss +12 -11
- package/components/hy-coupon/props.ts +5 -5
- package/components/hy-folding-panel/hy-folding-panel-group.vue +163 -0
- package/components/hy-index-bar/hy-index-bar.vue +197 -0
- package/components/hy-index-bar/index.scss +65 -0
- package/components/hy-index-bar/props.ts +94 -0
- package/components/hy-index-bar/typing.d.ts +36 -0
- package/components/hy-number-step/hy-number-step.vue +367 -367
- package/components/hy-number-step/index.scss +14 -5
- package/components/hy-qrcode/qrcode.js.bak +1434 -0
- package/components/hy-table/hy-table.vue +30 -13
- package/components/hy-table/index.scss +6 -1
- package/components/hy-table/props.ts +1 -1
- package/components/hy-tag/index.scss +5 -13
- package/components/hy-text/hy-text.vue +1 -1
- package/global.d.ts +92 -91
- package/libs/utils/utils.ts +503 -521
- package/package.json +2 -2
- package/web-types.json +1 -1
|
@@ -1,367 +1,367 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<view class="hy-number-box">
|
|
3
|
-
<!-- 减号 -->
|
|
4
|
-
<view
|
|
5
|
-
v-if="showMinus && !hideMinus && $slots.minus"
|
|
6
|
-
class="hy-number-box__slot cursor-pointer"
|
|
7
|
-
@tap.stop="clickHandler('minus')"
|
|
8
|
-
@touchstart="onTouchStart('minus')"
|
|
9
|
-
@touchend.stop="onClearTimeout"
|
|
10
|
-
>
|
|
11
|
-
<slot name="minus" />
|
|
12
|
-
</view>
|
|
13
|
-
<view
|
|
14
|
-
v-else-if="showMinus && !hideMinus"
|
|
15
|
-
@tap.stop="clickHandler('minus')"
|
|
16
|
-
@touchstart="onTouchStart('minus')"
|
|
17
|
-
@touchend.stop="onClearTimeout"
|
|
18
|
-
hover-class="hy-number-box__minus--hover"
|
|
19
|
-
hover-stay-time="150"
|
|
20
|
-
:class="[
|
|
21
|
-
{ 'hy-number-box__minus--disabled': isDisabled('minus') },
|
|
22
|
-
'hy-number-box__minus',
|
|
23
|
-
'cursor-pointer'
|
|
24
|
-
]"
|
|
25
|
-
:style="buttonStyle('minus')"
|
|
26
|
-
>
|
|
27
|
-
<hy-icon
|
|
28
|
-
:name="minusIcon?.name || IconConfig.MINUS"
|
|
29
|
-
:color="isDisabled('minus') ? '#c8c9cc' : minusIcon?.color"
|
|
30
|
-
:size="minusIcon?.size"
|
|
31
|
-
:bold="minusIcon?.bold"
|
|
32
|
-
:customPrefix="minusIcon?.customPrefix"
|
|
33
|
-
:imgMode="minusIcon?.imgMode"
|
|
34
|
-
:width="minusIcon?.width"
|
|
35
|
-
:height="minusIcon?.height"
|
|
36
|
-
:top="minusIcon?.top"
|
|
37
|
-
:stop="minusIcon?.stop"
|
|
38
|
-
:round="minusIcon?.round"
|
|
39
|
-
:customStyle="minusIcon?.customStyle"
|
|
40
|
-
></hy-icon>
|
|
41
|
-
</view>
|
|
42
|
-
<!-- 减号 -->
|
|
43
|
-
|
|
44
|
-
<template v-if="!hideMinus">
|
|
45
|
-
<!-- 中间输入框组件 -->
|
|
46
|
-
<slot v-if="$slots.input" name="input" :record="currentValue"></slot>
|
|
47
|
-
<template v-else>
|
|
48
|
-
<!-- #ifdef MP-WEIXIN -->
|
|
49
|
-
<input
|
|
50
|
-
:disabled="disabledInput || disabled"
|
|
51
|
-
:cursor-spacing="getCursorSpacing"
|
|
52
|
-
:class="[
|
|
53
|
-
{
|
|
54
|
-
'hy-number-box__input--disabled': disabled || disabledInput
|
|
55
|
-
},
|
|
56
|
-
'hy-number-box__input'
|
|
57
|
-
]"
|
|
58
|
-
:value="currentValue"
|
|
59
|
-
@blur="onBlur"
|
|
60
|
-
@focus="onFocus"
|
|
61
|
-
@input="onInput"
|
|
62
|
-
type="number"
|
|
63
|
-
:style="[inputStyle]"
|
|
64
|
-
/>
|
|
65
|
-
<!-- #endif -->
|
|
66
|
-
<!-- #ifndef MP-WEIXIN -->
|
|
67
|
-
<input
|
|
68
|
-
:disabled="disabledInput || disabled"
|
|
69
|
-
:cursor-spacing="getCursorSpacing"
|
|
70
|
-
:class="[
|
|
71
|
-
{
|
|
72
|
-
'hy-number-box__input--disabled': disabled || disabledInput
|
|
73
|
-
},
|
|
74
|
-
'hy-number-box__input'
|
|
75
|
-
]"
|
|
76
|
-
v-model="currentValue"
|
|
77
|
-
@blur="onBlur"
|
|
78
|
-
@focus="onFocus"
|
|
79
|
-
@input="onInput"
|
|
80
|
-
type="number"
|
|
81
|
-
:style="[inputStyle]"
|
|
82
|
-
/>
|
|
83
|
-
<!-- #endif -->
|
|
84
|
-
</template>
|
|
85
|
-
</template>
|
|
86
|
-
|
|
87
|
-
<!-- 加号 -->
|
|
88
|
-
<view
|
|
89
|
-
class="hy-number-box__slot cursor-pointer"
|
|
90
|
-
@tap.stop="clickHandler('plus')"
|
|
91
|
-
@touchstart="onTouchStart('plus')"
|
|
92
|
-
v-if="showPlus && $slots.plus"
|
|
93
|
-
@touchend.stop="onClearTimeout"
|
|
94
|
-
>
|
|
95
|
-
<slot name="plus" />
|
|
96
|
-
</view>
|
|
97
|
-
<view
|
|
98
|
-
v-else-if="showPlus"
|
|
99
|
-
@tap.stop="clickHandler('plus')"
|
|
100
|
-
@touchstart="onTouchStart('plus')"
|
|
101
|
-
@touchend.stop="onClearTimeout"
|
|
102
|
-
hover-class="hy-number-box__plus--hover"
|
|
103
|
-
hover-stay-time="150"
|
|
104
|
-
:class="[
|
|
105
|
-
{ 'hy-number-
|
|
106
|
-
'hy-number-box__plus',
|
|
107
|
-
'cursor-pointer'
|
|
108
|
-
]"
|
|
109
|
-
:style="[buttonStyle('plus')]"
|
|
110
|
-
>
|
|
111
|
-
<hy-icon
|
|
112
|
-
:name="plusIcon?.name || IconConfig.PLUS"
|
|
113
|
-
:color="isDisabled('plus') ? '#c8c9cc' : plusIcon?.color"
|
|
114
|
-
:size="plusIcon?.size"
|
|
115
|
-
:bold="plusIcon?.bold"
|
|
116
|
-
:customPrefix="plusIcon?.customPrefix"
|
|
117
|
-
:imgMode="plusIcon?.imgMode"
|
|
118
|
-
:width="plusIcon?.width"
|
|
119
|
-
:height="plusIcon?.height"
|
|
120
|
-
:top="plusIcon?.top"
|
|
121
|
-
:stop="plusIcon?.stop"
|
|
122
|
-
:round="plusIcon?.round"
|
|
123
|
-
:customStyle="plusIcon?.customStyle"
|
|
124
|
-
></hy-icon>
|
|
125
|
-
</view>
|
|
126
|
-
<!-- 加号 -->
|
|
127
|
-
</view>
|
|
128
|
-
</template>
|
|
129
|
-
|
|
130
|
-
<script lang="ts">
|
|
131
|
-
export default {
|
|
132
|
-
name: 'hy-number-step',
|
|
133
|
-
options: {
|
|
134
|
-
addGlobalClass: true,
|
|
135
|
-
virtualHost: true,
|
|
136
|
-
styleIsolation: 'shared'
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
</script>
|
|
140
|
-
|
|
141
|
-
<script setup lang="ts">
|
|
142
|
-
import { computed, ref, watch, onMounted, nextTick } from 'vue'
|
|
143
|
-
import type { CSSProperties } from 'vue'
|
|
144
|
-
import { addUnit, IconConfig } from '../../libs'
|
|
145
|
-
import type { INumberStepEmits } from './typing'
|
|
146
|
-
import type { InputOnBlurEvent, InputOnFocusEvent, InputOnInputEvent } from '@uni-helper/uni-types'
|
|
147
|
-
import numberStepProps from './props'
|
|
148
|
-
// 组件
|
|
149
|
-
import HyIcon from '../hy-icon/hy-icon.vue'
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* 一般用于商城购物选择物品数量的场景
|
|
153
|
-
* @displayName hy-number-step
|
|
154
|
-
*/
|
|
155
|
-
defineOptions({})
|
|
156
|
-
|
|
157
|
-
const props = defineProps(numberStepProps)
|
|
158
|
-
const emit = defineEmits<INumberStepEmits>()
|
|
159
|
-
type StepType = 'plus' | 'minus'
|
|
160
|
-
|
|
161
|
-
// 输入框实际操作的值
|
|
162
|
-
const currentValue = ref<number>(0)
|
|
163
|
-
// 定时器
|
|
164
|
-
let longPressTimer: ReturnType<typeof setTimeout> | null = null
|
|
165
|
-
|
|
166
|
-
const stepType = ref<StepType>('plus')
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* @description 格式化整理数据,限制范围
|
|
170
|
-
* @param value 处理值
|
|
171
|
-
* */
|
|
172
|
-
const format = (value: number | string): number => {
|
|
173
|
-
// 如果为空字符串,那么设置为0,同时将值转为Number类型
|
|
174
|
-
value = !value ? 0 : +value
|
|
175
|
-
// 对比最大最小值,取在min和max之间的值
|
|
176
|
-
value = Math.max(Math.min(props.max, value), props.min)
|
|
177
|
-
// 如果设定了最大的小数位数,使用toFixed去进行格式化
|
|
178
|
-
if (props.decimalLength !== null) {
|
|
179
|
-
value = parseFloat(value.toFixed(props.decimalLength))
|
|
180
|
-
}
|
|
181
|
-
return value
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// 用于监听多个值发生变化
|
|
185
|
-
const watchChange = computed(() => {
|
|
186
|
-
return [props.integer, props.decimalLength, props.min, props.max]
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
watch(
|
|
190
|
-
() => watchChange.value,
|
|
191
|
-
() => check()
|
|
192
|
-
)
|
|
193
|
-
watch(
|
|
194
|
-
() => props.modelValue,
|
|
195
|
-
(newValue: number) => {
|
|
196
|
-
if (newValue !== currentValue.value) {
|
|
197
|
-
currentValue.value = format(props.modelValue)
|
|
198
|
-
}
|
|
199
|
-
},
|
|
200
|
-
{ immediate: true }
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
const hideMinus = computed(() => {
|
|
204
|
-
return currentValue.value == 0 && props.miniMode
|
|
205
|
-
})
|
|
206
|
-
const getCursorSpacing = computed(() => {
|
|
207
|
-
// 判断传入的单位,如果为px单位,需要转成px
|
|
208
|
-
return props.cursorSpacing
|
|
209
|
-
})
|
|
210
|
-
// 按钮的样式
|
|
211
|
-
const buttonStyle = computed(() => {
|
|
212
|
-
return (type: string) => {
|
|
213
|
-
const style: CSSProperties = {
|
|
214
|
-
backgroundColor: props.bgColor,
|
|
215
|
-
width: addUnit(props.buttonWidth),
|
|
216
|
-
height: addUnit(props.buttonSize),
|
|
217
|
-
color: props.color,
|
|
218
|
-
borderRadius: props.buttonRadius
|
|
219
|
-
}
|
|
220
|
-
return style
|
|
221
|
-
}
|
|
222
|
-
})
|
|
223
|
-
// 输入框的样式
|
|
224
|
-
const inputStyle = computed<CSSProperties>(() => {
|
|
225
|
-
// const disabled_1 = props.disabled || disabledInput.value;
|
|
226
|
-
return {
|
|
227
|
-
color: props.color,
|
|
228
|
-
backgroundColor: props.inputBgColor || props.bgColor,
|
|
229
|
-
height: addUnit(props.buttonSize),
|
|
230
|
-
width: addUnit(props.inputWidth)
|
|
231
|
-
}
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
const isDisabled = computed(() => {
|
|
235
|
-
return (type: string) => {
|
|
236
|
-
if (type === 'plus') {
|
|
237
|
-
// 在点击增加按钮情况下,判断整体的disabled,是否单独禁用增加按钮,以及当前值是否大于最大的允许值
|
|
238
|
-
return props.disabled || props.disablePlus || currentValue.value >= props.max
|
|
239
|
-
}
|
|
240
|
-
// 点击减少按钮同理
|
|
241
|
-
return props.disabled || props.disableMinus || currentValue.value <= props.min
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
onMounted(() => {
|
|
246
|
-
init()
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
const init = () => {
|
|
250
|
-
currentValue.value = format(props.modelValue)
|
|
251
|
-
}
|
|
252
|
-
const check = () => {
|
|
253
|
-
// 格式化了之后,如果前后的值不相等,那么设置为格式化后的值
|
|
254
|
-
const val = format(currentValue.value)
|
|
255
|
-
if (val != currentValue.value) {
|
|
256
|
-
currentValue.value = val
|
|
257
|
-
emitChange(val)
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* @description 输入框活动焦点
|
|
263
|
-
*/
|
|
264
|
-
const onFocus = (event: InputOnFocusEvent) => {
|
|
265
|
-
emit('focus', event.detail.value)
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* @description 输入框失去焦点
|
|
269
|
-
*/
|
|
270
|
-
const onBlur = (event: InputOnBlurEvent) => {
|
|
271
|
-
// 对输入值进行格式化
|
|
272
|
-
format(Number(event.detail.value))
|
|
273
|
-
// 发出blur事件
|
|
274
|
-
emit('blur', event.detail.value)
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* @description 输入框值发生变化
|
|
278
|
-
*/
|
|
279
|
-
const onInput = (e: InputOnInputEvent) => {
|
|
280
|
-
const { value = '' } = e.detail || {}
|
|
281
|
-
// 为空返回
|
|
282
|
-
if (value === '') return
|
|
283
|
-
let formatted = value
|
|
284
|
-
// https://github.com/ijry/uview-plus/issues/613
|
|
285
|
-
emitChange(value)
|
|
286
|
-
// 最大允许的小数长度
|
|
287
|
-
if (props.decimalLength !== null && formatted.indexOf('.') !== -1) {
|
|
288
|
-
const pair = formatted.split('.')
|
|
289
|
-
formatted = `${pair[0]}.${pair[1].slice(0, props.decimalLength)}`
|
|
290
|
-
}
|
|
291
|
-
formatted = format(formatted).toString()
|
|
292
|
-
emitChange(formatted)
|
|
293
|
-
// #ifdef MP-WEIXIN
|
|
294
|
-
return formatted
|
|
295
|
-
// #endif
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* @description 发出change事件
|
|
299
|
-
* @param value 值
|
|
300
|
-
*/
|
|
301
|
-
const emitChange = (value: number | string) => {
|
|
302
|
-
// 如果开启了异步变更值,则不修改内部的值,需要用户手动在外部通过v-model变更
|
|
303
|
-
if (!props.asyncChange) {
|
|
304
|
-
nextTick(() => {
|
|
305
|
-
emit('update:modelValue', value)
|
|
306
|
-
currentValue.value = Number(value)
|
|
307
|
-
// this.$forceUpdate()
|
|
308
|
-
})
|
|
309
|
-
}
|
|
310
|
-
emit('change', value)
|
|
311
|
-
}
|
|
312
|
-
const onChange = () => {
|
|
313
|
-
if (isDisabled.value(stepType.value)) {
|
|
314
|
-
return emit('overLimit', stepType.value)
|
|
315
|
-
}
|
|
316
|
-
const diff = stepType.value === 'minus' ? -props.step : +props.step
|
|
317
|
-
const value = format(add(+currentValue.value, diff))
|
|
318
|
-
emitChange(value)
|
|
319
|
-
emit(stepType.value, value)
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* @description 对值扩大后进行四舍五入,再除以扩大因子,避免出现浮点数操作的精度问题
|
|
323
|
-
* @param num1
|
|
324
|
-
* @param num2
|
|
325
|
-
* */
|
|
326
|
-
const add = (num1: number, num2: number) => {
|
|
327
|
-
const cardinal = Math.pow(10, 10)
|
|
328
|
-
return Math.round((num1 + num2) * cardinal) / cardinal
|
|
329
|
-
}
|
|
330
|
-
// 点击加减按钮
|
|
331
|
-
const clickHandler = (type: StepType) => {
|
|
332
|
-
stepType.value = type
|
|
333
|
-
onChange()
|
|
334
|
-
}
|
|
335
|
-
const longPressStep = () => {
|
|
336
|
-
// 每隔一段时间,重新调用longPressStep方法,实现长按加减
|
|
337
|
-
onClearTimeout()
|
|
338
|
-
longPressTimer = setTimeout(() => {
|
|
339
|
-
onChange()
|
|
340
|
-
longPressStep()
|
|
341
|
-
}, 250)
|
|
342
|
-
}
|
|
343
|
-
const onTouchStart = (type: StepType) => {
|
|
344
|
-
if (!props.longPress) return
|
|
345
|
-
onClearTimeout()
|
|
346
|
-
stepType.value = type
|
|
347
|
-
// 一定时间后,默认达到长按状态
|
|
348
|
-
longPressTimer = setTimeout(() => {
|
|
349
|
-
onChange()
|
|
350
|
-
longPressStep()
|
|
351
|
-
}, 600)
|
|
352
|
-
}
|
|
353
|
-
// 触摸结束,清除定时器,停止长按加减
|
|
354
|
-
const onTouchEnd = () => {
|
|
355
|
-
if (!props.longPress) return
|
|
356
|
-
onClearTimeout()
|
|
357
|
-
}
|
|
358
|
-
// 清除定时器
|
|
359
|
-
const onClearTimeout = () => {
|
|
360
|
-
clearTimeout(longPressTimer as number)
|
|
361
|
-
longPressTimer = null
|
|
362
|
-
}
|
|
363
|
-
</script>
|
|
364
|
-
|
|
365
|
-
<style lang="scss" scoped>
|
|
366
|
-
@import './index.scss';
|
|
367
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<view class="hy-number-box">
|
|
3
|
+
<!-- 减号 -->
|
|
4
|
+
<view
|
|
5
|
+
v-if="showMinus && !hideMinus && $slots.minus"
|
|
6
|
+
class="hy-number-box__slot cursor-pointer"
|
|
7
|
+
@tap.stop="clickHandler('minus')"
|
|
8
|
+
@touchstart="onTouchStart('minus')"
|
|
9
|
+
@touchend.stop="onClearTimeout"
|
|
10
|
+
>
|
|
11
|
+
<slot name="minus" />
|
|
12
|
+
</view>
|
|
13
|
+
<view
|
|
14
|
+
v-else-if="showMinus && !hideMinus"
|
|
15
|
+
@tap.stop="clickHandler('minus')"
|
|
16
|
+
@touchstart="onTouchStart('minus')"
|
|
17
|
+
@touchend.stop="onClearTimeout"
|
|
18
|
+
hover-class="hy-number-box__minus--hover"
|
|
19
|
+
hover-stay-time="150"
|
|
20
|
+
:class="[
|
|
21
|
+
{ 'hy-number-box__minus--disabled': isDisabled('minus') },
|
|
22
|
+
'hy-number-box__minus',
|
|
23
|
+
'cursor-pointer'
|
|
24
|
+
]"
|
|
25
|
+
:style="buttonStyle('minus')"
|
|
26
|
+
>
|
|
27
|
+
<hy-icon
|
|
28
|
+
:name="minusIcon?.name || IconConfig.MINUS"
|
|
29
|
+
:color="isDisabled('minus') ? '#c8c9cc' : minusIcon?.color"
|
|
30
|
+
:size="minusIcon?.size"
|
|
31
|
+
:bold="minusIcon?.bold"
|
|
32
|
+
:customPrefix="minusIcon?.customPrefix"
|
|
33
|
+
:imgMode="minusIcon?.imgMode"
|
|
34
|
+
:width="minusIcon?.width"
|
|
35
|
+
:height="minusIcon?.height"
|
|
36
|
+
:top="minusIcon?.top"
|
|
37
|
+
:stop="minusIcon?.stop"
|
|
38
|
+
:round="minusIcon?.round"
|
|
39
|
+
:customStyle="minusIcon?.customStyle"
|
|
40
|
+
></hy-icon>
|
|
41
|
+
</view>
|
|
42
|
+
<!-- 减号 -->
|
|
43
|
+
|
|
44
|
+
<template v-if="!hideMinus">
|
|
45
|
+
<!-- 中间输入框组件 -->
|
|
46
|
+
<slot v-if="$slots.input" name="input" :record="currentValue"></slot>
|
|
47
|
+
<template v-else>
|
|
48
|
+
<!-- #ifdef MP-WEIXIN -->
|
|
49
|
+
<input
|
|
50
|
+
:disabled="disabledInput || disabled"
|
|
51
|
+
:cursor-spacing="getCursorSpacing"
|
|
52
|
+
:class="[
|
|
53
|
+
{
|
|
54
|
+
'hy-number-box__input--disabled': disabled || disabledInput
|
|
55
|
+
},
|
|
56
|
+
'hy-number-box__input'
|
|
57
|
+
]"
|
|
58
|
+
:value="currentValue"
|
|
59
|
+
@blur="onBlur"
|
|
60
|
+
@focus="onFocus"
|
|
61
|
+
@input="onInput"
|
|
62
|
+
type="number"
|
|
63
|
+
:style="[inputStyle]"
|
|
64
|
+
/>
|
|
65
|
+
<!-- #endif -->
|
|
66
|
+
<!-- #ifndef MP-WEIXIN -->
|
|
67
|
+
<input
|
|
68
|
+
:disabled="disabledInput || disabled"
|
|
69
|
+
:cursor-spacing="getCursorSpacing"
|
|
70
|
+
:class="[
|
|
71
|
+
{
|
|
72
|
+
'hy-number-box__input--disabled': disabled || disabledInput
|
|
73
|
+
},
|
|
74
|
+
'hy-number-box__input'
|
|
75
|
+
]"
|
|
76
|
+
v-model="currentValue"
|
|
77
|
+
@blur="onBlur"
|
|
78
|
+
@focus="onFocus"
|
|
79
|
+
@input="onInput"
|
|
80
|
+
type="number"
|
|
81
|
+
:style="[inputStyle]"
|
|
82
|
+
/>
|
|
83
|
+
<!-- #endif -->
|
|
84
|
+
</template>
|
|
85
|
+
</template>
|
|
86
|
+
|
|
87
|
+
<!-- 加号 -->
|
|
88
|
+
<view
|
|
89
|
+
class="hy-number-box__slot cursor-pointer"
|
|
90
|
+
@tap.stop="clickHandler('plus')"
|
|
91
|
+
@touchstart="onTouchStart('plus')"
|
|
92
|
+
v-if="showPlus && $slots.plus"
|
|
93
|
+
@touchend.stop="onClearTimeout"
|
|
94
|
+
>
|
|
95
|
+
<slot name="plus" />
|
|
96
|
+
</view>
|
|
97
|
+
<view
|
|
98
|
+
v-else-if="showPlus"
|
|
99
|
+
@tap.stop="clickHandler('plus')"
|
|
100
|
+
@touchstart="onTouchStart('plus')"
|
|
101
|
+
@touchend.stop="onClearTimeout"
|
|
102
|
+
hover-class="hy-number-box__plus--hover"
|
|
103
|
+
hover-stay-time="150"
|
|
104
|
+
:class="[
|
|
105
|
+
{ 'hy-number-box__plus--disabled': isDisabled('plus') },
|
|
106
|
+
'hy-number-box__plus',
|
|
107
|
+
'cursor-pointer'
|
|
108
|
+
]"
|
|
109
|
+
:style="[buttonStyle('plus')]"
|
|
110
|
+
>
|
|
111
|
+
<hy-icon
|
|
112
|
+
:name="plusIcon?.name || IconConfig.PLUS"
|
|
113
|
+
:color="isDisabled('plus') ? '#c8c9cc' : plusIcon?.color"
|
|
114
|
+
:size="plusIcon?.size"
|
|
115
|
+
:bold="plusIcon?.bold"
|
|
116
|
+
:customPrefix="plusIcon?.customPrefix"
|
|
117
|
+
:imgMode="plusIcon?.imgMode"
|
|
118
|
+
:width="plusIcon?.width"
|
|
119
|
+
:height="plusIcon?.height"
|
|
120
|
+
:top="plusIcon?.top"
|
|
121
|
+
:stop="plusIcon?.stop"
|
|
122
|
+
:round="plusIcon?.round"
|
|
123
|
+
:customStyle="plusIcon?.customStyle"
|
|
124
|
+
></hy-icon>
|
|
125
|
+
</view>
|
|
126
|
+
<!-- 加号 -->
|
|
127
|
+
</view>
|
|
128
|
+
</template>
|
|
129
|
+
|
|
130
|
+
<script lang="ts">
|
|
131
|
+
export default {
|
|
132
|
+
name: 'hy-number-step',
|
|
133
|
+
options: {
|
|
134
|
+
addGlobalClass: true,
|
|
135
|
+
virtualHost: true,
|
|
136
|
+
styleIsolation: 'shared'
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<script setup lang="ts">
|
|
142
|
+
import { computed, ref, watch, onMounted, nextTick } from 'vue'
|
|
143
|
+
import type { CSSProperties } from 'vue'
|
|
144
|
+
import { addUnit, IconConfig } from '../../libs'
|
|
145
|
+
import type { INumberStepEmits } from './typing'
|
|
146
|
+
import type { InputOnBlurEvent, InputOnFocusEvent, InputOnInputEvent } from '@uni-helper/uni-types'
|
|
147
|
+
import numberStepProps from './props'
|
|
148
|
+
// 组件
|
|
149
|
+
import HyIcon from '../hy-icon/hy-icon.vue'
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 一般用于商城购物选择物品数量的场景
|
|
153
|
+
* @displayName hy-number-step
|
|
154
|
+
*/
|
|
155
|
+
defineOptions({})
|
|
156
|
+
|
|
157
|
+
const props = defineProps(numberStepProps)
|
|
158
|
+
const emit = defineEmits<INumberStepEmits>()
|
|
159
|
+
type StepType = 'plus' | 'minus'
|
|
160
|
+
|
|
161
|
+
// 输入框实际操作的值
|
|
162
|
+
const currentValue = ref<number>(0)
|
|
163
|
+
// 定时器
|
|
164
|
+
let longPressTimer: ReturnType<typeof setTimeout> | null = null
|
|
165
|
+
|
|
166
|
+
const stepType = ref<StepType>('plus')
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @description 格式化整理数据,限制范围
|
|
170
|
+
* @param value 处理值
|
|
171
|
+
* */
|
|
172
|
+
const format = (value: number | string): number => {
|
|
173
|
+
// 如果为空字符串,那么设置为0,同时将值转为Number类型
|
|
174
|
+
value = !value ? 0 : +value
|
|
175
|
+
// 对比最大最小值,取在min和max之间的值
|
|
176
|
+
value = Math.max(Math.min(props.max, value), props.min)
|
|
177
|
+
// 如果设定了最大的小数位数,使用toFixed去进行格式化
|
|
178
|
+
if (props.decimalLength !== null) {
|
|
179
|
+
value = parseFloat(value.toFixed(props.decimalLength))
|
|
180
|
+
}
|
|
181
|
+
return value
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 用于监听多个值发生变化
|
|
185
|
+
const watchChange = computed(() => {
|
|
186
|
+
return [props.integer, props.decimalLength, props.min, props.max]
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
watch(
|
|
190
|
+
() => watchChange.value,
|
|
191
|
+
() => check()
|
|
192
|
+
)
|
|
193
|
+
watch(
|
|
194
|
+
() => props.modelValue,
|
|
195
|
+
(newValue: number) => {
|
|
196
|
+
if (newValue !== currentValue.value) {
|
|
197
|
+
currentValue.value = format(props.modelValue)
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
{ immediate: true }
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
const hideMinus = computed(() => {
|
|
204
|
+
return currentValue.value == 0 && props.miniMode
|
|
205
|
+
})
|
|
206
|
+
const getCursorSpacing = computed(() => {
|
|
207
|
+
// 判断传入的单位,如果为px单位,需要转成px
|
|
208
|
+
return props.cursorSpacing
|
|
209
|
+
})
|
|
210
|
+
// 按钮的样式
|
|
211
|
+
const buttonStyle = computed(() => {
|
|
212
|
+
return (type: string) => {
|
|
213
|
+
const style: CSSProperties = {
|
|
214
|
+
backgroundColor: props.bgColor,
|
|
215
|
+
width: addUnit(props.buttonWidth),
|
|
216
|
+
height: addUnit(props.buttonSize),
|
|
217
|
+
color: props.color,
|
|
218
|
+
borderRadius: props.buttonRadius
|
|
219
|
+
}
|
|
220
|
+
return style
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
// 输入框的样式
|
|
224
|
+
const inputStyle = computed<CSSProperties>(() => {
|
|
225
|
+
// const disabled_1 = props.disabled || disabledInput.value;
|
|
226
|
+
return {
|
|
227
|
+
color: props.color,
|
|
228
|
+
backgroundColor: props.inputBgColor || props.bgColor,
|
|
229
|
+
height: addUnit(props.buttonSize),
|
|
230
|
+
width: addUnit(props.inputWidth)
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const isDisabled = computed(() => {
|
|
235
|
+
return (type: string) => {
|
|
236
|
+
if (type === 'plus') {
|
|
237
|
+
// 在点击增加按钮情况下,判断整体的disabled,是否单独禁用增加按钮,以及当前值是否大于最大的允许值
|
|
238
|
+
return props.disabled || props.disablePlus || currentValue.value >= props.max
|
|
239
|
+
}
|
|
240
|
+
// 点击减少按钮同理
|
|
241
|
+
return props.disabled || props.disableMinus || currentValue.value <= props.min
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
onMounted(() => {
|
|
246
|
+
init()
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
const init = () => {
|
|
250
|
+
currentValue.value = format(props.modelValue)
|
|
251
|
+
}
|
|
252
|
+
const check = () => {
|
|
253
|
+
// 格式化了之后,如果前后的值不相等,那么设置为格式化后的值
|
|
254
|
+
const val = format(currentValue.value)
|
|
255
|
+
if (val != currentValue.value) {
|
|
256
|
+
currentValue.value = val
|
|
257
|
+
emitChange(val)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* @description 输入框活动焦点
|
|
263
|
+
*/
|
|
264
|
+
const onFocus = (event: InputOnFocusEvent) => {
|
|
265
|
+
emit('focus', event.detail.value)
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* @description 输入框失去焦点
|
|
269
|
+
*/
|
|
270
|
+
const onBlur = (event: InputOnBlurEvent) => {
|
|
271
|
+
// 对输入值进行格式化
|
|
272
|
+
format(Number(event.detail.value))
|
|
273
|
+
// 发出blur事件
|
|
274
|
+
emit('blur', event.detail.value)
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* @description 输入框值发生变化
|
|
278
|
+
*/
|
|
279
|
+
const onInput = (e: InputOnInputEvent) => {
|
|
280
|
+
const { value = '' } = e.detail || {}
|
|
281
|
+
// 为空返回
|
|
282
|
+
if (value === '') return
|
|
283
|
+
let formatted = value
|
|
284
|
+
// https://github.com/ijry/uview-plus/issues/613
|
|
285
|
+
emitChange(value)
|
|
286
|
+
// 最大允许的小数长度
|
|
287
|
+
if (props.decimalLength !== null && formatted.indexOf('.') !== -1) {
|
|
288
|
+
const pair = formatted.split('.')
|
|
289
|
+
formatted = `${pair[0]}.${pair[1].slice(0, props.decimalLength)}`
|
|
290
|
+
}
|
|
291
|
+
formatted = format(formatted).toString()
|
|
292
|
+
emitChange(formatted)
|
|
293
|
+
// #ifdef MP-WEIXIN
|
|
294
|
+
return formatted
|
|
295
|
+
// #endif
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* @description 发出change事件
|
|
299
|
+
* @param value 值
|
|
300
|
+
*/
|
|
301
|
+
const emitChange = (value: number | string) => {
|
|
302
|
+
// 如果开启了异步变更值,则不修改内部的值,需要用户手动在外部通过v-model变更
|
|
303
|
+
if (!props.asyncChange) {
|
|
304
|
+
nextTick(() => {
|
|
305
|
+
emit('update:modelValue', value)
|
|
306
|
+
currentValue.value = Number(value)
|
|
307
|
+
// this.$forceUpdate()
|
|
308
|
+
})
|
|
309
|
+
}
|
|
310
|
+
emit('change', value)
|
|
311
|
+
}
|
|
312
|
+
const onChange = () => {
|
|
313
|
+
if (isDisabled.value(stepType.value)) {
|
|
314
|
+
return emit('overLimit', stepType.value)
|
|
315
|
+
}
|
|
316
|
+
const diff = stepType.value === 'minus' ? -props.step : +props.step
|
|
317
|
+
const value = format(add(+currentValue.value, diff))
|
|
318
|
+
emitChange(value)
|
|
319
|
+
emit(stepType.value, value)
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* @description 对值扩大后进行四舍五入,再除以扩大因子,避免出现浮点数操作的精度问题
|
|
323
|
+
* @param num1
|
|
324
|
+
* @param num2
|
|
325
|
+
* */
|
|
326
|
+
const add = (num1: number, num2: number) => {
|
|
327
|
+
const cardinal = Math.pow(10, 10)
|
|
328
|
+
return Math.round((num1 + num2) * cardinal) / cardinal
|
|
329
|
+
}
|
|
330
|
+
// 点击加减按钮
|
|
331
|
+
const clickHandler = (type: StepType) => {
|
|
332
|
+
stepType.value = type
|
|
333
|
+
onChange()
|
|
334
|
+
}
|
|
335
|
+
const longPressStep = () => {
|
|
336
|
+
// 每隔一段时间,重新调用longPressStep方法,实现长按加减
|
|
337
|
+
onClearTimeout()
|
|
338
|
+
longPressTimer = setTimeout(() => {
|
|
339
|
+
onChange()
|
|
340
|
+
longPressStep()
|
|
341
|
+
}, 250)
|
|
342
|
+
}
|
|
343
|
+
const onTouchStart = (type: StepType) => {
|
|
344
|
+
if (!props.longPress) return
|
|
345
|
+
onClearTimeout()
|
|
346
|
+
stepType.value = type
|
|
347
|
+
// 一定时间后,默认达到长按状态
|
|
348
|
+
longPressTimer = setTimeout(() => {
|
|
349
|
+
onChange()
|
|
350
|
+
longPressStep()
|
|
351
|
+
}, 600)
|
|
352
|
+
}
|
|
353
|
+
// 触摸结束,清除定时器,停止长按加减
|
|
354
|
+
const onTouchEnd = () => {
|
|
355
|
+
if (!props.longPress) return
|
|
356
|
+
onClearTimeout()
|
|
357
|
+
}
|
|
358
|
+
// 清除定时器
|
|
359
|
+
const onClearTimeout = () => {
|
|
360
|
+
clearTimeout(longPressTimer as number)
|
|
361
|
+
longPressTimer = null
|
|
362
|
+
}
|
|
363
|
+
</script>
|
|
364
|
+
|
|
365
|
+
<style lang="scss" scoped>
|
|
366
|
+
@import './index.scss';
|
|
367
|
+
</style>
|