vue2server7 7.0.8 → 7.0.11
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/frontEnd/src/components/NumberRange.vue +107 -143
- package/package.json +1 -1
- package/test/docs.zip +0 -0
- package/test/element-plus.zip +0 -0
|
@@ -1,90 +1,3 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
NumberRange 数字区间输入组件
|
|
3
|
-
用于表单中输入数值范围(如金额区间、年龄区间等)
|
|
4
|
-
|
|
5
|
-
═══════════════════════════════════════════════════
|
|
6
|
-
两种绑定模式
|
|
7
|
-
═══════════════════════════════════════════════════
|
|
8
|
-
|
|
9
|
-
1. 数组模式 —— v-model 绑定 [min, max] 数组
|
|
10
|
-
<NumberRange v-model="form.amountRange" />
|
|
11
|
-
|
|
12
|
-
2. 双字段模式 —— v-model:start / v-model:end 分别绑定
|
|
13
|
-
<NumberRange v-model:start="form.amountMin" v-model:end="form.amountMax" />
|
|
14
|
-
|
|
15
|
-
═══════════════════════════════════════════════════
|
|
16
|
-
Props
|
|
17
|
-
═══════════════════════════════════════════════════
|
|
18
|
-
|
|
19
|
-
| Prop | 类型 | 默认值 | 说明 |
|
|
20
|
-
|────────────────|─────────|─────────────|───────────────────────────────|
|
|
21
|
-
| modelValue | Array | undefined | 数组模式绑定值 [min, max] |
|
|
22
|
-
| start | Number | undefined | 双字段模式起始值 |
|
|
23
|
-
| end | Number | undefined | 双字段模式结束值 |
|
|
24
|
-
| minPlaceholder | String | '最小值' | 起始输入框占位文本 |
|
|
25
|
-
| maxPlaceholder | String | '最大值' | 结束输入框占位文本 |
|
|
26
|
-
| separator | String | '到' | 两个输入框之间的分隔文字 |
|
|
27
|
-
| min | Number | -Infinity | 允许输入的最小边界 |
|
|
28
|
-
| max | Number | Infinity | 允许输入的最大边界 |
|
|
29
|
-
| precision | Number | undefined | 小数位数限制(不传则不限制) |
|
|
30
|
-
| disabled | Boolean | false | 是否禁用 |
|
|
31
|
-
|
|
32
|
-
═══════════════════════════════════════════════════
|
|
33
|
-
Events
|
|
34
|
-
═══════════════════════════════════════════════════
|
|
35
|
-
|
|
36
|
-
| Event | 参数 | 说明 |
|
|
37
|
-
|────────────────────|─────────────────|───────────────────────────────|
|
|
38
|
-
| update:modelValue | [min, max] | 数组模式值变更 |
|
|
39
|
-
| update:start | number | null | 双字段模式起始值变更 |
|
|
40
|
-
| update:end | number | null | 双字段模式结束值变更 |
|
|
41
|
-
| change | [min, max] | 值变更(两种模式都触发) |
|
|
42
|
-
|
|
43
|
-
═══════════════════════════════════════════════════
|
|
44
|
-
Expose(通过 ref 调用)
|
|
45
|
-
═══════════════════════════════════════════════════
|
|
46
|
-
|
|
47
|
-
| 属性/方法 | 类型 | 说明 |
|
|
48
|
-
|───────────|───────────────────|───────────────────────|
|
|
49
|
-
| validate | () => boolean | 手动触发校验 |
|
|
50
|
-
| isValid | ComputedRef<bool> | 当前区间是否合法 |
|
|
51
|
-
|
|
52
|
-
═══════════════════════════════════════════════════
|
|
53
|
-
配套校验规则(配合 el-form 使用)
|
|
54
|
-
═══════════════════════════════════════════════════
|
|
55
|
-
|
|
56
|
-
import { rangeRule, rangeRequired } from '@/components/NumberRange.vue'
|
|
57
|
-
|
|
58
|
-
- rangeRequired(message?, startField?, trigger?)
|
|
59
|
-
区间必填校验
|
|
60
|
-
数组模式: rangeRequired('请输入金额范围')
|
|
61
|
-
双字段模式: rangeRequired('请输入年龄范围', 'ageMin')
|
|
62
|
-
|
|
63
|
-
- rangeRule(message?, startField?, trigger?)
|
|
64
|
-
区间大小校验(结束值 >= 起始值)
|
|
65
|
-
数组模式: rangeRule('结束金额不能小于起始金额')
|
|
66
|
-
双字段模式: rangeRule('结束年龄不能小于起始年龄', 'ageMin')
|
|
67
|
-
|
|
68
|
-
═══════════════════════════════════════════════════
|
|
69
|
-
完整示例
|
|
70
|
-
═══════════════════════════════════════════════════
|
|
71
|
-
|
|
72
|
-
<!-- 数组模式 -->
|
|
73
|
-
<el-form-item prop="amountRange" label="金额范围">
|
|
74
|
-
<NumberRange v-model="form.amountRange" :precision="2"
|
|
75
|
-
min-placeholder="最小金额" max-placeholder="最大金额" />
|
|
76
|
-
</el-form-item>
|
|
77
|
-
rules: { amountRange: [rangeRule('结束金额不能小于起始金额')] }
|
|
78
|
-
|
|
79
|
-
<!-- 双字段模式 -->
|
|
80
|
-
<el-form-item prop="ageMax" label="年龄范围" required>
|
|
81
|
-
<NumberRange v-model:start="form.ageMin" v-model:end="form.ageMax"
|
|
82
|
-
:precision="0" min-placeholder="最小年龄" max-placeholder="最大年龄" />
|
|
83
|
-
</el-form-item>
|
|
84
|
-
rules: { ageMax: [rangeRequired('请输入年龄范围', 'ageMin'),
|
|
85
|
-
rangeRule('结束年龄不能小于起始年龄', 'ageMin')] }
|
|
86
|
-
-->
|
|
87
|
-
|
|
88
1
|
<template>
|
|
89
2
|
<div class="number-range" :class="{ 'is-disabled': disabled }">
|
|
90
3
|
<el-input
|
|
@@ -113,74 +26,126 @@
|
|
|
113
26
|
</div>
|
|
114
27
|
</template>
|
|
115
28
|
|
|
116
|
-
<script>
|
|
29
|
+
<script lang="ts">
|
|
30
|
+
import type { FormItemRule } from 'element-plus'
|
|
31
|
+
|
|
32
|
+
type RangeValue = [number | null, number | null]
|
|
33
|
+
type ValidatorCb = (error?: Error) => void
|
|
34
|
+
|
|
117
35
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
36
|
+
* NumberRange 数字区间输入组件
|
|
37
|
+
*
|
|
38
|
+
* 两种绑定模式:
|
|
39
|
+
* 数组模式 v-model="form.amountRange"
|
|
40
|
+
* 双字段模式 v-model:start="form.ageMin" v-model:end="form.ageMax"
|
|
41
|
+
*
|
|
42
|
+
* 配套校验规则(配合 el-form 使用):
|
|
43
|
+
* import { rangeRule, rangeRequired } from '@/components/NumberRange.vue'
|
|
44
|
+
*
|
|
45
|
+
* 数组模式:
|
|
46
|
+
* rangeRequired('请输入金额范围')
|
|
47
|
+
* rangeRule('结束金额不能小于起始金额')
|
|
48
|
+
*
|
|
49
|
+
* 双字段模式:
|
|
50
|
+
* rangeRequired('请输入年龄范围', 'ageMin')
|
|
51
|
+
* rangeRule('结束年龄不能小于起始年龄', 'ageMin')
|
|
122
52
|
*/
|
|
123
|
-
|
|
53
|
+
|
|
54
|
+
/** 区间必填校验规则 */
|
|
55
|
+
export function rangeRequired(
|
|
56
|
+
message = '请输入完整区间',
|
|
57
|
+
startField?: string,
|
|
58
|
+
trigger = 'change'
|
|
59
|
+
): FormItemRule {
|
|
124
60
|
return {
|
|
125
61
|
trigger,
|
|
126
62
|
validator: startField
|
|
127
|
-
? (_r, _v
|
|
128
|
-
|
|
63
|
+
? (_r: unknown, _v: unknown, cb: ValidatorCb, source: Record<string, unknown>) =>
|
|
64
|
+
cb(source[startField] == null || source[startField] === '' ? new Error(message) : undefined)
|
|
65
|
+
: (_r: unknown, v: unknown, cb: ValidatorCb) => {
|
|
66
|
+
const arr = v as RangeValue | undefined
|
|
67
|
+
cb(!Array.isArray(arr) || arr[0] == null || arr[1] == null ? new Error(message) : undefined)
|
|
68
|
+
}
|
|
129
69
|
}
|
|
130
70
|
}
|
|
131
71
|
|
|
132
|
-
/**
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
export function rangeRule(message = '结束值不能小于起始值', startField, trigger = 'change') {
|
|
72
|
+
/** 区间大小校验规则(结束值 >= 起始值) */
|
|
73
|
+
export function rangeRule(
|
|
74
|
+
message = '结束值不能小于起始值',
|
|
75
|
+
startField?: string,
|
|
76
|
+
trigger = 'change'
|
|
77
|
+
): FormItemRule {
|
|
139
78
|
return {
|
|
140
79
|
trigger,
|
|
141
80
|
validator: startField
|
|
142
|
-
? (_r, v, cb, source
|
|
143
|
-
|
|
81
|
+
? (_r: unknown, v: unknown, cb: ValidatorCb, source: Record<string, unknown>) =>
|
|
82
|
+
cb(source[startField] != null && v != null && (v as number) < (source[startField] as number) ? new Error(message) : undefined)
|
|
83
|
+
: (_r: unknown, v: unknown, cb: ValidatorCb) => {
|
|
84
|
+
const arr = v as RangeValue | undefined
|
|
85
|
+
cb(arr?.[0] != null && arr?.[1] != null && arr[1] < arr[0] ? new Error(message) : undefined)
|
|
86
|
+
}
|
|
144
87
|
}
|
|
145
88
|
}
|
|
146
89
|
</script>
|
|
147
90
|
|
|
148
|
-
<script setup>
|
|
91
|
+
<script setup lang="ts">
|
|
149
92
|
import { ref, watch, computed } from 'vue'
|
|
150
93
|
|
|
151
|
-
|
|
94
|
+
type Nullable<T> = T | null | undefined
|
|
95
|
+
|
|
96
|
+
interface Props {
|
|
152
97
|
/** 数组模式绑定值 [min, max],与 start/end 二选一 */
|
|
153
|
-
modelValue
|
|
98
|
+
modelValue?: RangeValue
|
|
154
99
|
/** 双字段模式 - 起始值 */
|
|
155
|
-
start
|
|
100
|
+
start?: number | null
|
|
156
101
|
/** 双字段模式 - 结束值 */
|
|
157
|
-
end
|
|
158
|
-
minPlaceholder
|
|
159
|
-
maxPlaceholder
|
|
160
|
-
|
|
102
|
+
end?: number | null
|
|
103
|
+
minPlaceholder?: string
|
|
104
|
+
maxPlaceholder?: string
|
|
105
|
+
/** 两个输入框之间的分隔文字 */
|
|
106
|
+
separator?: string
|
|
161
107
|
/** 允许输入的最小边界 */
|
|
162
|
-
min
|
|
108
|
+
min?: number
|
|
163
109
|
/** 允许输入的最大边界 */
|
|
164
|
-
max
|
|
165
|
-
step
|
|
110
|
+
max?: number
|
|
111
|
+
step?: number
|
|
166
112
|
/** 小数位数限制,如 2 表示最多两位小数 */
|
|
167
|
-
precision
|
|
168
|
-
disabled
|
|
113
|
+
precision?: number
|
|
114
|
+
disabled?: boolean
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
118
|
+
modelValue: undefined,
|
|
119
|
+
start: undefined,
|
|
120
|
+
end: undefined,
|
|
121
|
+
minPlaceholder: '最小值',
|
|
122
|
+
maxPlaceholder: '最大值',
|
|
123
|
+
separator: '到',
|
|
124
|
+
min: -Infinity,
|
|
125
|
+
max: Infinity,
|
|
126
|
+
step: 1,
|
|
127
|
+
precision: undefined,
|
|
128
|
+
disabled: false
|
|
169
129
|
})
|
|
170
130
|
|
|
171
|
-
const emit = defineEmits
|
|
131
|
+
const emit = defineEmits<{
|
|
132
|
+
'update:modelValue': [value: RangeValue]
|
|
133
|
+
'update:start': [value: number | null]
|
|
134
|
+
'update:end': [value: number | null]
|
|
135
|
+
change: [value: RangeValue]
|
|
136
|
+
}>()
|
|
172
137
|
|
|
173
138
|
/** 根据是否传入 modelValue 自动判断绑定模式 */
|
|
174
139
|
const isArrayMode = computed(() => props.modelValue !== undefined)
|
|
175
140
|
|
|
176
|
-
function getInitMin() {
|
|
141
|
+
function getInitMin(): Nullable<number> {
|
|
177
142
|
return isArrayMode.value ? props.modelValue?.[0] : props.start
|
|
178
143
|
}
|
|
179
|
-
function getInitMax() {
|
|
144
|
+
function getInitMax(): Nullable<number> {
|
|
180
145
|
return isArrayMode.value ? props.modelValue?.[1] : props.end
|
|
181
146
|
}
|
|
182
147
|
|
|
183
|
-
function toDisplay(val) {
|
|
148
|
+
function toDisplay(val: Nullable<number>): string {
|
|
184
149
|
if (val === null || val === undefined) return ''
|
|
185
150
|
return String(val)
|
|
186
151
|
}
|
|
@@ -192,9 +157,9 @@ const maxFocused = ref(false)
|
|
|
192
157
|
|
|
193
158
|
/**
|
|
194
159
|
* 输入过滤:只允许数字、小数点、负号;
|
|
195
|
-
*
|
|
160
|
+
* 小数点仅保留第一个,小数位数受 precision 约束
|
|
196
161
|
*/
|
|
197
|
-
function sanitize(raw) {
|
|
162
|
+
function sanitize(raw: string | null | undefined): string {
|
|
198
163
|
if (raw === '' || raw === null || raw === undefined) return ''
|
|
199
164
|
let s = raw.replace(/[^0-9.\-]/g, '')
|
|
200
165
|
|
|
@@ -214,14 +179,14 @@ function sanitize(raw) {
|
|
|
214
179
|
return s
|
|
215
180
|
}
|
|
216
181
|
|
|
217
|
-
function parseNum(val) {
|
|
182
|
+
function parseNum(val: string | null | undefined): number | null {
|
|
218
183
|
if (val === '' || val === null || val === undefined) return null
|
|
219
184
|
const n = Number(val)
|
|
220
185
|
return Number.isNaN(n) ? null : n
|
|
221
186
|
}
|
|
222
187
|
|
|
223
188
|
/** 按 precision 格式化数值 */
|
|
224
|
-
function formatNum(val) {
|
|
189
|
+
function formatNum(val: number | null | undefined): number | null {
|
|
225
190
|
if (val === null || val === undefined) return null
|
|
226
191
|
if (props.precision !== undefined) {
|
|
227
192
|
return Number(Number(val).toFixed(props.precision))
|
|
@@ -238,7 +203,7 @@ const rangeInvalid = computed(() => {
|
|
|
238
203
|
const isValid = computed(() => !rangeInvalid.value)
|
|
239
204
|
|
|
240
205
|
/** 根据绑定模式 emit 对应事件 */
|
|
241
|
-
function emitValue() {
|
|
206
|
+
function emitValue(): void {
|
|
242
207
|
const minVal = formatNum(parseNum(minDisplay.value))
|
|
243
208
|
const maxVal = formatNum(parseNum(maxDisplay.value))
|
|
244
209
|
if (isArrayMode.value) {
|
|
@@ -250,40 +215,40 @@ function emitValue() {
|
|
|
250
215
|
emit('change', [minVal, maxVal])
|
|
251
216
|
}
|
|
252
217
|
|
|
253
|
-
function onMinInput(val) {
|
|
254
|
-
minDisplay.value = sanitize(val)
|
|
218
|
+
function onMinInput(val: string | number): void {
|
|
219
|
+
minDisplay.value = sanitize(String(val))
|
|
255
220
|
}
|
|
256
221
|
|
|
257
|
-
function onMaxInput(val) {
|
|
258
|
-
maxDisplay.value = sanitize(val)
|
|
222
|
+
function onMaxInput(val: string | number): void {
|
|
223
|
+
maxDisplay.value = sanitize(String(val))
|
|
259
224
|
}
|
|
260
225
|
|
|
261
|
-
function onMinClear() {
|
|
226
|
+
function onMinClear(): void {
|
|
262
227
|
minDisplay.value = ''
|
|
263
228
|
emitValue()
|
|
264
229
|
}
|
|
265
230
|
|
|
266
|
-
function onMaxClear() {
|
|
231
|
+
function onMaxClear(): void {
|
|
267
232
|
maxDisplay.value = ''
|
|
268
233
|
emitValue()
|
|
269
234
|
}
|
|
270
235
|
|
|
271
|
-
function onMinFocus() {
|
|
236
|
+
function onMinFocus(): void {
|
|
272
237
|
minFocused.value = true
|
|
273
238
|
}
|
|
274
239
|
|
|
275
|
-
function onMaxFocus() {
|
|
240
|
+
function onMaxFocus(): void {
|
|
276
241
|
maxFocused.value = true
|
|
277
242
|
}
|
|
278
243
|
|
|
279
244
|
/** 失焦时格式化数值、钳位到 [min, max] 边界,并触发 emit */
|
|
280
|
-
function onMinBlur() {
|
|
245
|
+
function onMinBlur(): void {
|
|
281
246
|
minFocused.value = false
|
|
282
247
|
let v = parseNum(minDisplay.value)
|
|
283
248
|
if (v !== null) {
|
|
284
249
|
if (v < props.min) v = props.min
|
|
285
250
|
if (v > props.max) v = props.max
|
|
286
|
-
v = formatNum(v)
|
|
251
|
+
v = formatNum(v)!
|
|
287
252
|
minDisplay.value = String(v)
|
|
288
253
|
} else {
|
|
289
254
|
minDisplay.value = ''
|
|
@@ -291,13 +256,13 @@ function onMinBlur() {
|
|
|
291
256
|
emitValue()
|
|
292
257
|
}
|
|
293
258
|
|
|
294
|
-
function onMaxBlur() {
|
|
259
|
+
function onMaxBlur(): void {
|
|
295
260
|
maxFocused.value = false
|
|
296
261
|
let v = parseNum(maxDisplay.value)
|
|
297
262
|
if (v !== null) {
|
|
298
263
|
if (v > props.max) v = props.max
|
|
299
264
|
if (v < props.min) v = props.min
|
|
300
|
-
v = formatNum(v)
|
|
265
|
+
v = formatNum(v)!
|
|
301
266
|
maxDisplay.value = String(v)
|
|
302
267
|
} else {
|
|
303
268
|
maxDisplay.value = ''
|
|
@@ -305,7 +270,7 @@ function onMaxBlur() {
|
|
|
305
270
|
emitValue()
|
|
306
271
|
}
|
|
307
272
|
|
|
308
|
-
function validate() {
|
|
273
|
+
function validate(): boolean {
|
|
309
274
|
return isValid.value
|
|
310
275
|
}
|
|
311
276
|
|
|
@@ -315,12 +280,11 @@ defineExpose({ validate, isValid })
|
|
|
315
280
|
watch(
|
|
316
281
|
() => isArrayMode.value ? props.modelValue : [props.start, props.end],
|
|
317
282
|
(val) => {
|
|
318
|
-
const newMin = toDisplay(val?.[0])
|
|
319
|
-
const newMax = toDisplay(val?.[1])
|
|
283
|
+
const newMin = toDisplay(val?.[0] ?? null)
|
|
284
|
+
const newMax = toDisplay(val?.[1] ?? null)
|
|
320
285
|
if (newMin !== minDisplay.value) minDisplay.value = newMin
|
|
321
286
|
if (newMax !== maxDisplay.value) maxDisplay.value = newMax
|
|
322
|
-
if ((val?.[0]
|
|
323
|
-
(val?.[1] === null || val?.[1] === undefined)) {
|
|
287
|
+
if ((val?.[0] == null) && (val?.[1] == null)) {
|
|
324
288
|
minFocused.value = false
|
|
325
289
|
maxFocused.value = false
|
|
326
290
|
}
|
package/package.json
CHANGED
package/test/docs.zip
DELETED
|
Binary file
|
package/test/element-plus.zip
DELETED
|
Binary file
|