v-uni-app-ui 1.0.4 → 1.0.6
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/README.md +62 -82
- package/dist/v-uni-app-ui.css +1 -0
- package/dist/v-uni-app-ui.es.js +6569 -0
- package/dist/v-uni-app-ui.umd.js +7 -0
- package/package.json +17 -5
- package/components/config/css/basic.scss +0 -19
- package/components/config/interface/basic-type.js +0 -16
- package/components/config/interface/components-interface.ts +0 -0
- package/components/config/interface/monitor/components/input-monitor.js +0 -0
- package/components/config/interface/monitor/property-monitor.ts +0 -136
- package/components/config/interface/props/basic-props.ts +0 -88
- package/components/config/interface/props/components/button-props.ts +0 -85
- package/components/config/interface/props/components/input-props.ts +0 -69
- package/components/config/interface/props/props-tools.ts +0 -64
- package/components/config/style/basic.js +0 -346
- package/components/config/style/component-registry.js +0 -142
- package/components/config/style/components/button-style.js +0 -160
- package/components/config/style/components/input-style.js +0 -98
- package/components/config/style/components-style.js +0 -622
- package/components/config/style/property-mapper.js +0 -377
- package/components/config/style/pseudo-processor.js +0 -213
- package/components/config.js +0 -123
- package/components/icon/iconfont.css +0 -87
- package/components/icon/iconfont.js +0 -1
- package/components/icon/iconfont.json +0 -135
- package/components/icon/iconfont.ttf +0 -0
- package/components/icon/iconfont.woff +0 -0
- package/components/icon/iconfont.woff2 +0 -0
- package/components/layout/v-card/v-card.vue +0 -108
- package/components/layout/v-grid/v-grid.vue +0 -162
- package/components/layout/v-icon-grid/v-icon-grid.vue +0 -195
- package/components/layout/v-infinite-scroll/v-infinite-scroll.vue +0 -172
- package/components/layout/v-list/v-list.vue +0 -43
- package/components/layout/v-row/v-row.vue +0 -142
- package/components/layout/v-waterfall/v-waterfall.vue +0 -79
- package/components/model/compound/v-checkbox-group/v-checkbox-group.vue +0 -96
- package/components/model/compound/v-console/v-console.js +0 -20
- package/components/model/compound/v-console/v-console.vue +0 -299
- package/components/model/compound/v-date-time/v-date-time.vue +0 -261
- package/components/model/compound/v-dialog/v-dialog.vue +0 -178
- package/components/model/compound/v-drum-select-picker/v-drum-select-picker.vue +0 -83
- package/components/model/compound/v-form/v-form.vue +0 -226
- package/components/model/compound/v-form-item/v-form-item.vue +0 -255
- package/components/model/compound/v-image/v-image.vue +0 -357
- package/components/model/compound/v-input-desensitize/v-input-desensitize.vue +0 -101
- package/components/model/compound/v-page/v-page.vue +0 -11
- package/components/model/compound/v-pages/v-pages.vue +0 -141
- package/components/model/compound/v-picker-list/v-picker-list.vue +0 -109
- package/components/model/compound/v-popup/v-popup.vue +0 -151
- package/components/model/compound/v-radio-group/v-radio-group.vue +0 -86
- package/components/model/compound/v-select-picker/v-select-picker.vue +0 -202
- package/components/model/compound/v-series-picker-list/v-series-picker-list.vue +0 -221
- package/components/model/compound/v-series-select-picker/v-series-select-picker.vue +0 -203
- package/components/model/compound/v-switch/v-switch.vue +0 -136
- package/components/model/compound/v-tabs-page/v-tabs-page.vue +0 -138
- package/components/model/native/v-badge/v-badge.vue +0 -143
- package/components/model/native/v-button/v-button.vue +0 -81
- package/components/model/native/v-carousel/v-carousel.vue +0 -138
- package/components/model/native/v-checkbox/v-checkbox.vue +0 -215
- package/components/model/native/v-collapse/v-collapse.vue +0 -190
- package/components/model/native/v-header-navigation-bar/v-header-navigation-bar.vue +0 -92
- package/components/model/native/v-input/v-input.vue +0 -163
- package/components/model/native/v-input-code/v-input-code.vue +0 -146
- package/components/model/native/v-loading/v-loading.vue +0 -206
- package/components/model/native/v-menu/v-menu.vue +0 -222
- package/components/model/native/v-menu-slide/v-menu-slide.vue +0 -364
- package/components/model/native/v-min-loading/v-min-loading.vue +0 -80
- package/components/model/native/v-null/v-null.vue +0 -97
- package/components/model/native/v-overlay/v-overlay.vue +0 -96
- package/components/model/native/v-pull-up-refresh/v-pull-up-refresh.vue +0 -157
- package/components/model/native/v-radio/v-radio.vue +0 -138
- package/components/model/native/v-scroll-list/v-scroll-list.vue +0 -169
- package/components/model/native/v-steps/v-steps.vue +0 -253
- package/components/model/native/v-table/v-table.vue +0 -203
- package/components/model/native/v-tabs/v-tabs.vue +0 -235
- package/components/model/native/v-tag/v-tag.vue +0 -206
- package/components/model/native/v-text/v-text.vue +0 -187
- package/components/model/native/v-textarea/v-textarea.vue +0 -178
- package/components/model/native/v-title/v-title.vue +0 -91
- package/components/model/native/v-toast/info.png +0 -0
- package/components/model/native/v-toast/success.png +0 -0
- package/components/model/native/v-toast/v-toast.vue +0 -198
- package/components/model/native/v-toast/warn.png +0 -0
- package/components/model/native/v-upload-file-button/v-upload-file-button.vue +0 -296
- package/components/model/native/v-video/v-video.vue +0 -175
- package/components/model/native/v-window/v-window.vue +0 -158
- package/components/utils/event-modifiers.ts +0 -139
- package/components/utils/validator.ts +0 -451
- package/index.js +0 -372
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { ref, onUnmounted } from 'vue';
|
|
2
|
-
|
|
3
|
-
// ========== 类型定义 ==========
|
|
4
|
-
|
|
5
|
-
type TimerHandle = ReturnType<typeof setTimeout>;
|
|
6
|
-
type IntervalHandle = ReturnType<typeof setInterval>;
|
|
7
|
-
|
|
8
|
-
export interface DebounceOptions {
|
|
9
|
-
/** 防抖等待时间(毫秒) */
|
|
10
|
-
wait: number;
|
|
11
|
-
/** 是否立即执行第一次 */
|
|
12
|
-
immediate?: boolean;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface CooldownOptions {
|
|
16
|
-
/** 总冷却时间(毫秒) */
|
|
17
|
-
stabilizationTime: number;
|
|
18
|
-
/** 每次倒计时减少的值 */
|
|
19
|
-
degressionTime: number;
|
|
20
|
-
/** 倒计时更新间隔 */
|
|
21
|
-
intervalUpdateTime: number;
|
|
22
|
-
/** 是否在冷却期间点击时重置计时器 */
|
|
23
|
-
resetOnClick?: boolean;
|
|
24
|
-
/** 倒计时回调 */
|
|
25
|
-
onCountdown?: (payload: { remaining: number; total: number }) => void;
|
|
26
|
-
/** 冷却完成回调 */
|
|
27
|
-
onComplete?: () => void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ========== 防抖 Hook ==========
|
|
31
|
-
|
|
32
|
-
export function useDebounce<T extends (...args: any[]) => any>(callback: T, options: DebounceOptions) {
|
|
33
|
-
const timer = ref<TimerHandle | null>(null);
|
|
34
|
-
const isPending = ref(false);
|
|
35
|
-
|
|
36
|
-
function debounce(...args: Parameters<T>) {
|
|
37
|
-
if (timer.value) clearTimeout(timer.value);
|
|
38
|
-
|
|
39
|
-
const shouldCallNow = options.immediate && !isPending.value;
|
|
40
|
-
|
|
41
|
-
isPending.value = true;
|
|
42
|
-
timer.value = setTimeout(() => {
|
|
43
|
-
if (!options.immediate) {
|
|
44
|
-
callback(...args);
|
|
45
|
-
}
|
|
46
|
-
isPending.value = false;
|
|
47
|
-
}, options.wait);
|
|
48
|
-
|
|
49
|
-
if (shouldCallNow) {
|
|
50
|
-
callback(...args);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function cancel() {
|
|
55
|
-
if (timer.value) {
|
|
56
|
-
clearTimeout(timer.value);
|
|
57
|
-
timer.value = null;
|
|
58
|
-
}
|
|
59
|
-
isPending.value = false;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
onUnmounted(() => {
|
|
63
|
-
cancel();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
debounce,
|
|
68
|
-
cancel,
|
|
69
|
-
isPending
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// ========== 冷却 Hook ==========
|
|
74
|
-
|
|
75
|
-
export function useCooldown(options: CooldownOptions) {
|
|
76
|
-
const countdownTimer = ref<IntervalHandle | null>(null);
|
|
77
|
-
const cooldownTimer = ref<TimerHandle | null>(null);
|
|
78
|
-
const countdown = ref(options.stabilizationTime);
|
|
79
|
-
const isCountingDown = ref(false);
|
|
80
|
-
|
|
81
|
-
function stop() {
|
|
82
|
-
if (countdownTimer.value) {
|
|
83
|
-
clearInterval(countdownTimer.value);
|
|
84
|
-
countdownTimer.value = null;
|
|
85
|
-
}
|
|
86
|
-
if (cooldownTimer.value) {
|
|
87
|
-
clearTimeout(cooldownTimer.value);
|
|
88
|
-
cooldownTimer.value = null;
|
|
89
|
-
}
|
|
90
|
-
isCountingDown.value = false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function start() {
|
|
94
|
-
// 如果已在冷却中且允许重置,则重新开始
|
|
95
|
-
if (isCountingDown.value && options.resetOnClick) {
|
|
96
|
-
stop();
|
|
97
|
-
} else if (isCountingDown.value) {
|
|
98
|
-
return; // 冷却中且不允许重置,直接返回
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
isCountingDown.value = true;
|
|
102
|
-
countdown.value = options.stabilizationTime;
|
|
103
|
-
|
|
104
|
-
// 立即触发一次
|
|
105
|
-
options.onCountdown?.({
|
|
106
|
-
remaining: countdown.value,
|
|
107
|
-
total: options.stabilizationTime
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// 倒计时 interval
|
|
111
|
-
countdownTimer.value = setInterval(() => {
|
|
112
|
-
countdown.value -= options.degressionTime;
|
|
113
|
-
|
|
114
|
-
const remaining = Math.max(0, countdown.value);
|
|
115
|
-
options.onCountdown?.({ remaining, total: options.stabilizationTime });
|
|
116
|
-
|
|
117
|
-
if (countdown.value <= 0) {
|
|
118
|
-
stop();
|
|
119
|
-
options.onComplete?.();
|
|
120
|
-
}
|
|
121
|
-
}, options.intervalUpdateTime);
|
|
122
|
-
|
|
123
|
-
// 冷却结束定时器
|
|
124
|
-
cooldownTimer.value = setTimeout(() => {
|
|
125
|
-
isCountingDown.value = false;
|
|
126
|
-
}, options.stabilizationTime);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
onUnmounted(() => {
|
|
130
|
-
stop();
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
countdown,
|
|
135
|
-
isCountingDown,
|
|
136
|
-
start,
|
|
137
|
-
stop
|
|
138
|
-
};
|
|
139
|
-
}
|
|
@@ -1,451 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 验证器工具
|
|
3
|
-
* 提供常用数据验证函数
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// ==================== 基础验证器 ====================
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 创建枚举值验证器
|
|
10
|
-
* 验证值是否在指定的枚举值列表中
|
|
11
|
-
*
|
|
12
|
-
* @param allowedValues 允许的值数组
|
|
13
|
-
* @param errorMessage 错误提示信息(可选)
|
|
14
|
-
* @returns 验证器函数
|
|
15
|
-
*/
|
|
16
|
-
export function createEnumValidator<T extends string>(allowedValues: readonly T[], errorMessage?: string): (value: any) => value is T {
|
|
17
|
-
const validator = (value: any): value is T => {
|
|
18
|
-
const isValid = allowedValues.includes(value as T);
|
|
19
|
-
if (!isValid && errorMessage) {
|
|
20
|
-
console.warn(errorMessage, { value, allowedValues });
|
|
21
|
-
}
|
|
22
|
-
return isValid;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// 为验证器添加元数据
|
|
26
|
-
validator.allowedValues = allowedValues;
|
|
27
|
-
validator.errorMessage = errorMessage || `值必须在以下选项中: ${allowedValues.join(', ')}`;
|
|
28
|
-
|
|
29
|
-
return validator;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 创建数值范围验证器
|
|
34
|
-
* 验证数值是否在指定范围内
|
|
35
|
-
*
|
|
36
|
-
* @param minimum 最小值
|
|
37
|
-
* @param maximum 最大值
|
|
38
|
-
* @param errorMessage 错误提示信息(可选)
|
|
39
|
-
* @returns 验证器函数
|
|
40
|
-
*/
|
|
41
|
-
export function createNumberRangeValidator(minimum: number, maximum: number, errorMessage?: string): (value: number) => boolean {
|
|
42
|
-
const validator = (value: number): boolean => {
|
|
43
|
-
const isValid = value >= minimum && value <= maximum;
|
|
44
|
-
if (!isValid && errorMessage) {
|
|
45
|
-
console.warn(errorMessage, { value, minimum, maximum });
|
|
46
|
-
}
|
|
47
|
-
return isValid;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
validator.minimum = minimum;
|
|
51
|
-
validator.maximum = maximum;
|
|
52
|
-
validator.errorMessage = errorMessage || `数值必须在 ${minimum} 到 ${maximum} 之间`;
|
|
53
|
-
|
|
54
|
-
return validator;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 创建长度验证器
|
|
59
|
-
* 验证字符串或数组的长度
|
|
60
|
-
*
|
|
61
|
-
* @param minimumLength 最小长度(可选)
|
|
62
|
-
* @param maximumLength 最大长度(可选)
|
|
63
|
-
* @param errorMessage 错误提示信息(可选)
|
|
64
|
-
* @returns 验证器函数
|
|
65
|
-
*/
|
|
66
|
-
export function createLengthValidator(minimumLength?: number, maximumLength?: number, errorMessage?: string): (value: string | any[]) => boolean {
|
|
67
|
-
const validator = (value: string | any[]): boolean => {
|
|
68
|
-
const length = value.length;
|
|
69
|
-
|
|
70
|
-
if (minimumLength !== undefined && length < minimumLength) {
|
|
71
|
-
if (errorMessage) console.warn(errorMessage, { length, minimumLength, maximumLength });
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (maximumLength !== undefined && length > maximumLength) {
|
|
76
|
-
if (errorMessage) console.warn(errorMessage, { length, minimumLength, maximumLength });
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return true;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
validator.minimumLength = minimumLength;
|
|
84
|
-
validator.maximumLength = maximumLength;
|
|
85
|
-
validator.errorMessage = errorMessage || `长度必须在 ${minimumLength || '无限制'} 到 ${maximumLength || '无限制'} 之间`;
|
|
86
|
-
|
|
87
|
-
return validator;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* 创建正则表达式验证器
|
|
92
|
-
* 验证字符串是否匹配指定的正则表达式
|
|
93
|
-
*
|
|
94
|
-
* @param pattern 正则表达式
|
|
95
|
-
* @param errorMessage 错误提示信息(可选)
|
|
96
|
-
* @returns 验证器函数
|
|
97
|
-
*/
|
|
98
|
-
export function createPatternValidator(pattern: RegExp, errorMessage?: string): (value: string) => boolean {
|
|
99
|
-
const validator = (value: string): boolean => {
|
|
100
|
-
const isValid = pattern.test(value);
|
|
101
|
-
if (!isValid && errorMessage) {
|
|
102
|
-
console.warn(errorMessage, { value, pattern });
|
|
103
|
-
}
|
|
104
|
-
return isValid;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
validator.pattern = pattern;
|
|
108
|
-
validator.errorMessage = errorMessage || `必须匹配正则表达式: ${pattern}`;
|
|
109
|
-
|
|
110
|
-
return validator;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* 创建必填验证器
|
|
115
|
-
* 验证值是否为非空
|
|
116
|
-
*
|
|
117
|
-
* @param errorMessage 错误提示信息(可选)
|
|
118
|
-
* @returns 验证器函数
|
|
119
|
-
*/
|
|
120
|
-
export function createRequiredValidator(errorMessage?: string): (value: any) => boolean {
|
|
121
|
-
const validator = (value: any): boolean => {
|
|
122
|
-
let isValid = false;
|
|
123
|
-
|
|
124
|
-
if (value === undefined || value === null) {
|
|
125
|
-
isValid = false;
|
|
126
|
-
} else if (typeof value === 'string') {
|
|
127
|
-
isValid = value.trim() !== '';
|
|
128
|
-
} else if (Array.isArray(value)) {
|
|
129
|
-
isValid = value.length > 0;
|
|
130
|
-
} else if (typeof value === 'object') {
|
|
131
|
-
isValid = Object.keys(value).length > 0;
|
|
132
|
-
} else {
|
|
133
|
-
isValid = true;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (!isValid && errorMessage) {
|
|
137
|
-
console.warn(errorMessage, { value });
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return isValid;
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
validator.errorMessage = errorMessage || '此项为必填项';
|
|
144
|
-
|
|
145
|
-
return validator;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// ==================== 特定格式验证器 ====================
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* 创建电子邮箱验证器
|
|
152
|
-
* 验证电子邮箱格式
|
|
153
|
-
*
|
|
154
|
-
* @param errorMessage 错误提示信息(可选)
|
|
155
|
-
* @returns 验证器函数
|
|
156
|
-
*/
|
|
157
|
-
export function createEmailValidator(errorMessage?: string): (value: string) => boolean {
|
|
158
|
-
const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
159
|
-
|
|
160
|
-
const validator = (value: string): boolean => {
|
|
161
|
-
const isValid = emailPattern.test(value);
|
|
162
|
-
if (!isValid && errorMessage) {
|
|
163
|
-
console.warn(errorMessage, { value });
|
|
164
|
-
}
|
|
165
|
-
return isValid;
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
validator.errorMessage = errorMessage || '请输入有效的电子邮箱地址';
|
|
169
|
-
|
|
170
|
-
return validator;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* 创建手机号码验证器(中国大陆)
|
|
175
|
-
* 验证中国大陆手机号码格式
|
|
176
|
-
*
|
|
177
|
-
* @param errorMessage 错误提示信息(可选)
|
|
178
|
-
* @returns 验证器函数
|
|
179
|
-
*/
|
|
180
|
-
export function createPhoneNumberValidator(errorMessage?: string): (value: string) => boolean {
|
|
181
|
-
const phonePattern = /^1[3-9]\d{9}$/;
|
|
182
|
-
|
|
183
|
-
const validator = (value: string): boolean => {
|
|
184
|
-
const isValid = phonePattern.test(value);
|
|
185
|
-
if (!isValid && errorMessage) {
|
|
186
|
-
console.warn(errorMessage, { value });
|
|
187
|
-
}
|
|
188
|
-
return isValid;
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
validator.errorMessage = errorMessage || '请输入有效的手机号码(11位数字)';
|
|
192
|
-
|
|
193
|
-
return validator;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* 创建URL地址验证器
|
|
198
|
-
* 验证URL地址格式
|
|
199
|
-
*
|
|
200
|
-
* @param errorMessage 错误提示信息(可选)
|
|
201
|
-
* @returns 验证器函数
|
|
202
|
-
*/
|
|
203
|
-
export function createUrlValidator(errorMessage?: string): (value: string) => boolean {
|
|
204
|
-
const urlPattern = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;
|
|
205
|
-
|
|
206
|
-
const validator = (value: string): boolean => {
|
|
207
|
-
const isValid = urlPattern.test(value);
|
|
208
|
-
if (!isValid && errorMessage) {
|
|
209
|
-
console.warn(errorMessage, { value });
|
|
210
|
-
}
|
|
211
|
-
return isValid;
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
validator.errorMessage = errorMessage || '请输入有效的URL地址';
|
|
215
|
-
|
|
216
|
-
return validator;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* 创建身份证号码验证器(中国大陆)
|
|
221
|
-
* 验证中国大陆身份证号码格式
|
|
222
|
-
*
|
|
223
|
-
* @param errorMessage 错误提示信息(可选)
|
|
224
|
-
* @returns 验证器函数
|
|
225
|
-
*/
|
|
226
|
-
export function createIdCardValidator(errorMessage?: string): (value: string) => boolean {
|
|
227
|
-
const idCardPattern = /^\d{17}[\dXx]$/;
|
|
228
|
-
|
|
229
|
-
const validator = (value: string): boolean => {
|
|
230
|
-
const isValid = idCardPattern.test(value);
|
|
231
|
-
if (!isValid && errorMessage) {
|
|
232
|
-
console.warn(errorMessage, { value });
|
|
233
|
-
}
|
|
234
|
-
return isValid;
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
validator.errorMessage = errorMessage || '请输入有效的身份证号码(18位)';
|
|
238
|
-
|
|
239
|
-
return validator;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* 创建数值验证器
|
|
244
|
-
* 验证数值类型和范围
|
|
245
|
-
*
|
|
246
|
-
* @param options 配置选项
|
|
247
|
-
* @returns 验证器函数
|
|
248
|
-
*/
|
|
249
|
-
export function createNumberValidator(options?: {
|
|
250
|
-
minimum?: number;
|
|
251
|
-
maximum?: number;
|
|
252
|
-
integerOnly?: boolean;
|
|
253
|
-
positiveOnly?: boolean;
|
|
254
|
-
allowZero?: boolean;
|
|
255
|
-
errorMessage?: string;
|
|
256
|
-
}): (value: number) => boolean {
|
|
257
|
-
const validator = (value: number): boolean => {
|
|
258
|
-
// 检查是否为数值
|
|
259
|
-
if (typeof value !== 'number' || isNaN(value)) {
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// 检查是否为整数
|
|
264
|
-
if (options?.integerOnly && !Number.isInteger(value)) {
|
|
265
|
-
return false;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// 检查是否为正数
|
|
269
|
-
if (options?.positiveOnly && value <= 0) {
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// 检查是否允许零
|
|
274
|
-
if (!options?.allowZero && value === 0) {
|
|
275
|
-
return false;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// 检查最小值
|
|
279
|
-
if (options?.minimum !== undefined && value < options.minimum) {
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// 检查最大值
|
|
284
|
-
if (options?.maximum !== undefined && value > options.maximum) {
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return true;
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
validator.errorMessage = options?.errorMessage || '请输入有效的数值';
|
|
292
|
-
|
|
293
|
-
return validator;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// ==================== 组合验证器 ====================
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* 创建组合验证器
|
|
300
|
-
* 将多个验证器组合成一个验证器
|
|
301
|
-
*
|
|
302
|
-
* @param validators 验证器数组
|
|
303
|
-
* @returns 验证器函数
|
|
304
|
-
*/
|
|
305
|
-
export function createCombinedValidator<T>(...validators: ((value: T) => boolean)[]): (value: T) => boolean {
|
|
306
|
-
const validator = (value: T): boolean => {
|
|
307
|
-
for (const validatorFunc of validators) {
|
|
308
|
-
if (!validatorFunc(value)) {
|
|
309
|
-
return false;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return true;
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
validator.validators = validators;
|
|
316
|
-
|
|
317
|
-
return validator;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* 创建异步验证器
|
|
322
|
-
* 用于需要异步验证的场景
|
|
323
|
-
*
|
|
324
|
-
* @param validator 验证器函数
|
|
325
|
-
* @returns 异步验证器函数
|
|
326
|
-
*/
|
|
327
|
-
export function createAsyncValidator<T>(validator: (value: T) => boolean | Promise<boolean>): (value: T) => Promise<boolean> {
|
|
328
|
-
const asyncValidator = async (value: T): Promise<boolean> => {
|
|
329
|
-
try {
|
|
330
|
-
const result = validator(value);
|
|
331
|
-
return result instanceof Promise ? await result : result;
|
|
332
|
-
} catch (error) {
|
|
333
|
-
console.error('验证器执行出错:', error);
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
return asyncValidator;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// ==================== 预定义验证器集合 ====================
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* 预定义验证器集合
|
|
345
|
-
* 提供常用的验证器函数
|
|
346
|
-
*/
|
|
347
|
-
export const Validators = {
|
|
348
|
-
// 基础验证器
|
|
349
|
-
enum: createEnumValidator,
|
|
350
|
-
numberRange: createNumberRangeValidator,
|
|
351
|
-
length: createLengthValidator,
|
|
352
|
-
pattern: createPatternValidator,
|
|
353
|
-
required: createRequiredValidator,
|
|
354
|
-
|
|
355
|
-
// 格式验证器
|
|
356
|
-
email: createEmailValidator,
|
|
357
|
-
phoneNumber: createPhoneNumberValidator,
|
|
358
|
-
url: createUrlValidator,
|
|
359
|
-
idCard: createIdCardValidator,
|
|
360
|
-
number: createNumberValidator,
|
|
361
|
-
|
|
362
|
-
// 组合验证器
|
|
363
|
-
combine: createCombinedValidator,
|
|
364
|
-
async: createAsyncValidator,
|
|
365
|
-
|
|
366
|
-
// 预定义验证规则
|
|
367
|
-
// 正整数验证器
|
|
368
|
-
positiveInteger: createNumberValidator({
|
|
369
|
-
integerOnly: true,
|
|
370
|
-
positiveOnly: true,
|
|
371
|
-
allowZero: false,
|
|
372
|
-
errorMessage: '请输入正整数'
|
|
373
|
-
}),
|
|
374
|
-
|
|
375
|
-
// 非负整数验证器
|
|
376
|
-
nonNegativeInteger: createNumberValidator({
|
|
377
|
-
integerOnly: true,
|
|
378
|
-
positiveOnly: false,
|
|
379
|
-
allowZero: true,
|
|
380
|
-
errorMessage: '请输入非负整数'
|
|
381
|
-
}),
|
|
382
|
-
|
|
383
|
-
// 百分比验证器
|
|
384
|
-
percentage: createNumberValidator({
|
|
385
|
-
minimum: 0,
|
|
386
|
-
maximum: 100,
|
|
387
|
-
errorMessage: '请输入0到100之间的百分比数值'
|
|
388
|
-
}),
|
|
389
|
-
|
|
390
|
-
// 密码强度验证器(至少8位,包含字母和数字)
|
|
391
|
-
passwordStrength: createCombinedValidator<string>(
|
|
392
|
-
createLengthValidator(8, undefined, '密码长度至少8位'),
|
|
393
|
-
createPatternValidator(/[a-zA-Z]/, '密码必须包含字母'),
|
|
394
|
-
createPatternValidator(/[0-9]/, '密码必须包含数字')
|
|
395
|
-
),
|
|
396
|
-
|
|
397
|
-
// 用户名验证器(只能包含字母、数字、下划线,3-20位)
|
|
398
|
-
username: createCombinedValidator<string>(
|
|
399
|
-
createLengthValidator(3, 20, '用户名长度必须在3到20位之间'),
|
|
400
|
-
createPatternValidator(/^[a-zA-Z0-9_]+$/, '用户名只能包含字母、数字和下划线')
|
|
401
|
-
)
|
|
402
|
-
} as const;
|
|
403
|
-
|
|
404
|
-
// ==================== 验证器工具函数 ====================
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* 验证值是否符合规则
|
|
408
|
-
*
|
|
409
|
-
* @param value 要验证的值
|
|
410
|
-
* @param validator 验证器函数
|
|
411
|
-
* @returns 验证结果
|
|
412
|
-
*/
|
|
413
|
-
export function validateValue<T>(value: T, validator: (value: T) => boolean): { isValid: boolean; errorMessage?: string } {
|
|
414
|
-
const isValid = validator(value);
|
|
415
|
-
|
|
416
|
-
return {
|
|
417
|
-
isValid,
|
|
418
|
-
errorMessage: isValid ? undefined : (validator as any).errorMessage
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* 批量验证多个值
|
|
424
|
-
*
|
|
425
|
-
* @param values 值数组
|
|
426
|
-
* @param validator 验证器函数
|
|
427
|
-
* @returns 验证结果数组
|
|
428
|
-
*/
|
|
429
|
-
export function validateValues<T>(values: T[], validator: (value: T) => boolean): Array<{ value: T; isValid: boolean; errorMessage?: string }> {
|
|
430
|
-
return values.map((value) => ({
|
|
431
|
-
value,
|
|
432
|
-
...validateValue(value, validator)
|
|
433
|
-
}));
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* 创建自定义错误消息的验证器
|
|
438
|
-
*
|
|
439
|
-
* @param validator 原始验证器
|
|
440
|
-
* @param customErrorMessage 自定义错误消息
|
|
441
|
-
* @returns 带自定义错误消息的验证器
|
|
442
|
-
*/
|
|
443
|
-
export function withCustomErrorMessage<T>(validator: (value: T) => boolean, customErrorMessage: string): (value: T) => boolean {
|
|
444
|
-
const customValidator = (value: T): boolean => {
|
|
445
|
-
return validator(value);
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
customValidator.errorMessage = customErrorMessage;
|
|
449
|
-
|
|
450
|
-
return customValidator;
|
|
451
|
-
}
|