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.
@@ -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
- * 区间必填校验规则,用于 el-form rules
119
- * @param {string} message - 校验失败提示文案
120
- * @param {string} startField - 双字段模式下起始字段名;不传则为数组模式
121
- * @param {string} trigger - 触发方式,默认 'change'
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
- export function rangeRequired(message = '请输入完整区间', startField, trigger = 'change') {
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, cb, source) => cb(source[startField] == null || source[startField] === '' ? new Error(message) : undefined)
128
- : (_r, v, cb) => cb(!Array.isArray(v) || v[0] == null || v[1] == null ? new Error(message) : undefined)
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
- * 区间大小校验规则(结束值 >= 起始值),用于 el-form rules
134
- * @param {string} message - 校验失败提示文案
135
- * @param {string} startField - 双字段模式下起始字段名;不传则为数组模式
136
- * @param {string} trigger - 触发方式,默认 'change'
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) => cb(source[startField] != null && v != null && v < source[startField] ? new Error(message) : undefined)
143
- : (_r, v, cb) => cb(v?.[0] != null && v?.[1] != null && v[1] < v[0] ? new Error(message) : undefined)
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
- const props = defineProps({
94
+ type Nullable<T> = T | null | undefined
95
+
96
+ interface Props {
152
97
  /** 数组模式绑定值 [min, max],与 start/end 二选一 */
153
- modelValue: { type: Array, default: undefined },
98
+ modelValue?: RangeValue
154
99
  /** 双字段模式 - 起始值 */
155
- start: { type: Number, default: undefined },
100
+ start?: number | null
156
101
  /** 双字段模式 - 结束值 */
157
- end: { type: Number, default: undefined },
158
- minPlaceholder: { type: String, default: '最小值' },
159
- maxPlaceholder: { type: String, default: '最大值' },
160
- separator: { type: String, default: '到' },
102
+ end?: number | null
103
+ minPlaceholder?: string
104
+ maxPlaceholder?: string
105
+ /** 两个输入框之间的分隔文字 */
106
+ separator?: string
161
107
  /** 允许输入的最小边界 */
162
- min: { type: Number, default: -Infinity },
108
+ min?: number
163
109
  /** 允许输入的最大边界 */
164
- max: { type: Number, default: Infinity },
165
- step: { type: Number, default: 1 },
110
+ max?: number
111
+ step?: number
166
112
  /** 小数位数限制,如 2 表示最多两位小数 */
167
- precision: { type: Number, default: undefined },
168
- disabled: { type: Boolean, default: false }
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(['update:modelValue', 'update:start', 'update:end', 'change'])
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
- * 限制小数点只出现一次,小数位数受 precision 约束
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] === null || val?.[0] === undefined) &&
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2server7",
3
- "version": "7.0.8",
3
+ "version": "7.0.11",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "dev": "nodemon --watch src --ext ts --exec \"ts-node src/app.ts\"",
package/test/docs.zip DELETED
Binary file
Binary file