vue2server7 7.0.12 → 7.0.13
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.
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="number-range" :class="{ 'is-disabled': disabled }">
|
|
2
|
+
<div class="number-range" :class="{ 'is-disabled': disabled, 'is-error': showInternalError }">
|
|
3
3
|
<el-input
|
|
4
4
|
v-model="minDisplay"
|
|
5
5
|
class="number-range__input"
|
|
6
|
+
:class="{ 'is-error': showInternalError }"
|
|
6
7
|
:placeholder="minPlaceholder"
|
|
7
8
|
:disabled="disabled"
|
|
8
9
|
clearable
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
<el-input
|
|
16
17
|
v-model="maxDisplay"
|
|
17
18
|
class="number-range__input"
|
|
19
|
+
:class="{ 'is-error': showInternalError }"
|
|
18
20
|
:placeholder="maxPlaceholder"
|
|
19
21
|
:disabled="disabled"
|
|
20
22
|
clearable
|
|
@@ -23,22 +25,30 @@
|
|
|
23
25
|
@blur="onMaxBlur"
|
|
24
26
|
@clear="onMaxClear"
|
|
25
27
|
/>
|
|
28
|
+
<span v-if="showInternalError" class="number-range__error">结束值不能小于起始值</span>
|
|
26
29
|
</div>
|
|
27
30
|
</template>
|
|
28
31
|
|
|
29
32
|
<script lang="ts">
|
|
30
33
|
import type { FormItemRule } from 'element-plus'
|
|
31
34
|
|
|
32
|
-
export type RangeValue = [number | null, number | null]
|
|
35
|
+
export type RangeValue = [number | string | null, number | string | null]
|
|
33
36
|
type ValidatorCb = (error?: Error) => void
|
|
34
37
|
|
|
38
|
+
/** 将字符串/数字统一转为 number,空值返回 null */
|
|
39
|
+
function toNum(val: unknown): number | null {
|
|
40
|
+
if (val === null || val === undefined || val === '') return null
|
|
41
|
+
const n = Number(val)
|
|
42
|
+
return Number.isNaN(n) ? null : n
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
export interface NumberRangeProps {
|
|
36
|
-
/** 数组模式绑定值 [min, max],与 start/end
|
|
46
|
+
/** 数组模式绑定值 [min, max],与 start/end 二选一;值可以是 string 或 number */
|
|
37
47
|
modelValue?: RangeValue
|
|
38
|
-
/** 双字段模式 -
|
|
39
|
-
start?: number | null
|
|
40
|
-
/** 双字段模式 -
|
|
41
|
-
end?: number | null
|
|
48
|
+
/** 双字段模式 - 起始值(支持 string 类型数字) */
|
|
49
|
+
start?: number | string | null
|
|
50
|
+
/** 双字段模式 - 结束值(支持 string 类型数字) */
|
|
51
|
+
end?: number | string | null
|
|
42
52
|
minPlaceholder?: string
|
|
43
53
|
maxPlaceholder?: string
|
|
44
54
|
/** 两个输入框之间的分隔文字 */
|
|
@@ -82,10 +92,10 @@ export function rangeRequired(
|
|
|
82
92
|
trigger,
|
|
83
93
|
validator: startField
|
|
84
94
|
? (_r: unknown, _v: unknown, cb: ValidatorCb, source: Record<string, unknown>) =>
|
|
85
|
-
cb(source[startField] == null
|
|
95
|
+
cb(toNum(source[startField]) == null ? new Error(message) : undefined)
|
|
86
96
|
: (_r: unknown, v: unknown, cb: ValidatorCb) => {
|
|
87
97
|
const arr = v as RangeValue | undefined
|
|
88
|
-
cb(!Array.isArray(arr) || arr[0] == null || arr[1] == null ? new Error(message) : undefined)
|
|
98
|
+
cb(!Array.isArray(arr) || toNum(arr[0]) == null || toNum(arr[1]) == null ? new Error(message) : undefined)
|
|
89
99
|
}
|
|
90
100
|
}
|
|
91
101
|
}
|
|
@@ -99,18 +109,24 @@ export function rangeRule(
|
|
|
99
109
|
return {
|
|
100
110
|
trigger,
|
|
101
111
|
validator: startField
|
|
102
|
-
? (_r: unknown, v: unknown, cb: ValidatorCb, source: Record<string, unknown>) =>
|
|
103
|
-
|
|
112
|
+
? (_r: unknown, v: unknown, cb: ValidatorCb, source: Record<string, unknown>) => {
|
|
113
|
+
const startNum = toNum(source[startField])
|
|
114
|
+
const endNum = toNum(v)
|
|
115
|
+
cb(startNum != null && endNum != null && endNum < startNum ? new Error(message) : undefined)
|
|
116
|
+
}
|
|
104
117
|
: (_r: unknown, v: unknown, cb: ValidatorCb) => {
|
|
105
118
|
const arr = v as RangeValue | undefined
|
|
106
|
-
|
|
119
|
+
const minNum = toNum(arr?.[0])
|
|
120
|
+
const maxNum = toNum(arr?.[1])
|
|
121
|
+
cb(minNum != null && maxNum != null && maxNum < minNum ? new Error(message) : undefined)
|
|
107
122
|
}
|
|
108
123
|
}
|
|
109
124
|
}
|
|
110
125
|
</script>
|
|
111
126
|
|
|
112
127
|
<script setup lang="ts">
|
|
113
|
-
import { ref, watch, computed } from 'vue'
|
|
128
|
+
import { ref, watch, computed, inject } from 'vue'
|
|
129
|
+
import { formItemContextKey } from 'element-plus'
|
|
114
130
|
|
|
115
131
|
const props = withDefaults(defineProps<NumberRangeProps>(), {
|
|
116
132
|
modelValue: undefined,
|
|
@@ -133,18 +149,22 @@ const emit = defineEmits<{
|
|
|
133
149
|
change: [value: RangeValue]
|
|
134
150
|
}>()
|
|
135
151
|
|
|
152
|
+
/** 检测是否处于 el-form-item 内,有则交给 form 校验,无则组件内部校验 */
|
|
153
|
+
const formItemContext = inject(formItemContextKey, undefined)
|
|
154
|
+
const useInternalValidation = computed(() => !formItemContext)
|
|
155
|
+
|
|
136
156
|
/** 根据是否传入 modelValue 自动判断绑定模式 */
|
|
137
157
|
const isArrayMode = computed(() => props.modelValue !== undefined)
|
|
138
158
|
|
|
139
|
-
function getInitMin(): number | null
|
|
140
|
-
return isArrayMode.value ? props.modelValue?.[0] : props.start
|
|
159
|
+
function getInitMin(): number | null {
|
|
160
|
+
return toNum(isArrayMode.value ? props.modelValue?.[0] : props.start)
|
|
141
161
|
}
|
|
142
|
-
function getInitMax(): number | null
|
|
143
|
-
return isArrayMode.value ? props.modelValue?.[1] : props.end
|
|
162
|
+
function getInitMax(): number | null {
|
|
163
|
+
return toNum(isArrayMode.value ? props.modelValue?.[1] : props.end)
|
|
144
164
|
}
|
|
145
165
|
|
|
146
|
-
function toDisplay(val: number | null
|
|
147
|
-
if (val === null
|
|
166
|
+
function toDisplay(val: number | null): string {
|
|
167
|
+
if (val === null) return ''
|
|
148
168
|
return String(val)
|
|
149
169
|
}
|
|
150
170
|
|
|
@@ -200,6 +220,12 @@ const rangeInvalid = computed(() => {
|
|
|
200
220
|
|
|
201
221
|
const isValid = computed(() => !rangeInvalid.value)
|
|
202
222
|
|
|
223
|
+
/** 内部校验:仅在无 el-form-item 且两个输入框都失焦后展示 */
|
|
224
|
+
const blurred = ref(false)
|
|
225
|
+
const showInternalError = computed(() =>
|
|
226
|
+
useInternalValidation.value && blurred.value && rangeInvalid.value
|
|
227
|
+
)
|
|
228
|
+
|
|
203
229
|
/** 根据绑定模式 emit 对应事件 */
|
|
204
230
|
function emitValue(): void {
|
|
205
231
|
const minVal = formatNum(parseNum(minDisplay.value))
|
|
@@ -252,6 +278,7 @@ function onMinBlur(): void {
|
|
|
252
278
|
minDisplay.value = ''
|
|
253
279
|
}
|
|
254
280
|
emitValue()
|
|
281
|
+
if (!minFocused.value && !maxFocused.value) blurred.value = true
|
|
255
282
|
}
|
|
256
283
|
|
|
257
284
|
function onMaxBlur(): void {
|
|
@@ -266,9 +293,11 @@ function onMaxBlur(): void {
|
|
|
266
293
|
maxDisplay.value = ''
|
|
267
294
|
}
|
|
268
295
|
emitValue()
|
|
296
|
+
if (!minFocused.value && !maxFocused.value) blurred.value = true
|
|
269
297
|
}
|
|
270
298
|
|
|
271
299
|
function validate(): boolean {
|
|
300
|
+
blurred.value = true
|
|
272
301
|
return isValid.value
|
|
273
302
|
}
|
|
274
303
|
|
|
@@ -278,13 +307,14 @@ defineExpose({ validate, isValid })
|
|
|
278
307
|
watch(
|
|
279
308
|
() => isArrayMode.value ? props.modelValue : [props.start, props.end],
|
|
280
309
|
(val) => {
|
|
281
|
-
const newMin = toDisplay(val?.[0]
|
|
282
|
-
const newMax = toDisplay(val?.[1]
|
|
310
|
+
const newMin = toDisplay(toNum(val?.[0]))
|
|
311
|
+
const newMax = toDisplay(toNum(val?.[1]))
|
|
283
312
|
if (newMin !== minDisplay.value) minDisplay.value = newMin
|
|
284
313
|
if (newMax !== maxDisplay.value) maxDisplay.value = newMax
|
|
285
|
-
if ((val?.[0] == null
|
|
314
|
+
if (toNum(val?.[0]) == null && toNum(val?.[1]) == null) {
|
|
286
315
|
minFocused.value = false
|
|
287
316
|
maxFocused.value = false
|
|
317
|
+
blurred.value = false
|
|
288
318
|
}
|
|
289
319
|
},
|
|
290
320
|
{ deep: true }
|
|
@@ -296,12 +326,12 @@ watch(
|
|
|
296
326
|
display: inline-flex;
|
|
297
327
|
align-items: center;
|
|
298
328
|
gap: 8px;
|
|
299
|
-
|
|
300
|
-
position: relative;
|
|
329
|
+
width: 100%;
|
|
301
330
|
}
|
|
302
331
|
|
|
303
332
|
.number-range__input {
|
|
304
|
-
|
|
333
|
+
flex: 1;
|
|
334
|
+
min-width: 0;
|
|
305
335
|
}
|
|
306
336
|
|
|
307
337
|
.number-range__separator {
|
|
@@ -313,4 +343,16 @@ watch(
|
|
|
313
343
|
.number-range.is-disabled .number-range__separator {
|
|
314
344
|
color: var(--el-text-color-placeholder);
|
|
315
345
|
}
|
|
346
|
+
|
|
347
|
+
.number-range__input.is-error :deep(.el-input__wrapper) {
|
|
348
|
+
box-shadow: 0 0 0 1px var(--el-color-danger) inset;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.number-range__error {
|
|
352
|
+
width: 100%;
|
|
353
|
+
color: var(--el-color-danger);
|
|
354
|
+
font-size: 12px;
|
|
355
|
+
line-height: 1;
|
|
356
|
+
padding-top: 2px;
|
|
357
|
+
}
|
|
316
358
|
</style>
|