fantasia-ui 0.2.3 → 0.2.4

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.
@@ -0,0 +1,181 @@
1
+ /**
2
+ * 校验单个值是否符合规则列表(异步,支持 validator 返回 Promise)
3
+ * @param value 字段值
4
+ * @param rules 规则数组
5
+ * @returns 校验通过返回 undefined,失败返回错误信息字符串
6
+ */
7
+ export const validateSingleField = async (value, rules) => {
8
+ const ruleArray = Array.isArray(rules) ? rules : [rules];
9
+ for (let rule of ruleArray) {
10
+ // 0. Transform
11
+ if (rule.transform) {
12
+ value = rule.transform(value);
13
+ }
14
+ // 1. Required & Whitespace 校验
15
+ const isEmpty = value === undefined || value === null || value === '';
16
+ const isAllWhitespace = rule.whitespace && typeof value === 'string' && value.trim() === '';
17
+ if (rule.required && (isEmpty || isAllWhitespace)) {
18
+ return rule.message || '此字段为必填项';
19
+ }
20
+ // 非必填且没值,跳过后续校验
21
+ if (!rule.required && isEmpty)
22
+ continue;
23
+ // 2. 类型校验
24
+ if (rule.type) {
25
+ const typeError = checkType(value, rule);
26
+ if (typeError)
27
+ return typeError;
28
+ }
29
+ // 3. 范围校验 (数值比较 + 长度比较)
30
+ const rangeError = checkRange(value, rule);
31
+ if (rangeError)
32
+ return rangeError;
33
+ // 4. 正则校验 (独立于 type)
34
+ if (rule.pattern && !rule.pattern.test(String(value))) {
35
+ return rule.message || '格式不匹配';
36
+ }
37
+ // 5. 枚举校验
38
+ if (rule.enum && !rule.enum.includes(value)) {
39
+ return rule.message || '不在指定的枚举范围内';
40
+ }
41
+ // 6. 自定义校验器
42
+ if (rule.validator) {
43
+ const result = await Promise.resolve(rule.validator(value, rule));
44
+ if (typeof result === 'string')
45
+ return result;
46
+ if (result === false)
47
+ return rule.message || '校验失败';
48
+ }
49
+ }
50
+ return undefined;
51
+ };
52
+ /**
53
+ * 类型校验辅助函数
54
+ */
55
+ const checkType = (value, rule) => {
56
+ const { type, message } = rule;
57
+ switch (type) {
58
+ case 'string':
59
+ if (typeof value !== 'string')
60
+ return message || '必须是字符串';
61
+ break;
62
+ case 'number':
63
+ if (typeof value !== 'number' || isNaN(value))
64
+ return message || '必须是数字';
65
+ break;
66
+ case 'integer':
67
+ if (!Number.isInteger(value))
68
+ return message || '必须是整数';
69
+ break;
70
+ case 'boolean':
71
+ if (typeof value !== 'boolean')
72
+ return message || '必须是布尔值';
73
+ break;
74
+ case 'array':
75
+ if (!Array.isArray(value))
76
+ return message || '必须是数组';
77
+ break;
78
+ case 'object':
79
+ if (typeof value !== 'object' || value === null)
80
+ return message || '必须是对象';
81
+ break;
82
+ case 'email':
83
+ const emailRegex = /^[^\s@]+@([^\s@]+\.)+[^\s@]+$/;
84
+ if (!emailRegex.test(value))
85
+ return message || '邮箱格式不正确';
86
+ break;
87
+ case 'regexp':
88
+ if (rule.pattern && !rule.pattern.test(String(value))) {
89
+ return rule.message || '格式不匹配';
90
+ }
91
+ break;
92
+ }
93
+ return undefined;
94
+ };
95
+ const checkRange = (value, rule) => {
96
+ const { min, max, len, message, type } = rule;
97
+ const isNum = type === 'number' || type === 'integer' || type === 'float';
98
+ // 对于数字,校验数值大小;对于字符串/数组,校验 length
99
+ const valToMeasure = (isNum && typeof value === 'number') ? value : (value?.length);
100
+ if (valToMeasure === undefined)
101
+ return undefined;
102
+ if (len !== undefined && valToMeasure !== len) {
103
+ return message || (isNum ? `值必须等于 ${len}` : `长度必须为 ${len}`);
104
+ }
105
+ if (min !== undefined && valToMeasure < min) {
106
+ return message || (isNum ? `最小值不能小于 ${min}` : `长度不能小于 ${min}`);
107
+ }
108
+ if (max !== undefined && valToMeasure > max) {
109
+ return message || (isNum ? `最大值不能大于 ${max}` : `长度不能大于 ${max}`);
110
+ }
111
+ return undefined;
112
+ };
113
+ /**
114
+ * 批量校验整个 model(同步版本,不支持异步 validator,但调用单个异步校验实现)
115
+ * @param model 数据对象
116
+ * @param rules 规则集
117
+ * @returns 返回错误对象 { fieldName: errorMessage },无错误时返回空对象
118
+ */
119
+ export const validateModel = async (model, rules) => {
120
+ const errors = {};
121
+ // 获取属性值的辅助函数(支持 a.b.c)
122
+ const getValue = (obj, path) => {
123
+ return path.split('.').reduce((acc, key) => (acc ? acc[key] : undefined), obj);
124
+ };
125
+ const tasks = Object.entries(rules).map(async ([fieldName, fieldRules]) => {
126
+ const value = getValue(model, fieldName);
127
+ const errMsg = await validateSingleField(value, fieldRules);
128
+ if (errMsg) {
129
+ errors[fieldName] = errMsg;
130
+ }
131
+ });
132
+ await Promise.all(tasks);
133
+ return {
134
+ isValid: Object.keys(errors).length === 0,
135
+ errors
136
+ };
137
+ };
138
+ /**
139
+ * 同步版本的单个校验(不处理异步 validator,如果有异步会警告并跳过)
140
+ * 用于简单场景,不推荐在复杂表单中使用
141
+ */
142
+ export const validateSingleFieldSync = (value, rules) => {
143
+ if (!rules || rules.length === 0)
144
+ return undefined;
145
+ for (const rule of rules) {
146
+ if (rule.required) {
147
+ if (value === undefined || value === null || value === '') {
148
+ return rule.message || '此字段为必填项';
149
+ }
150
+ }
151
+ const isEmpty = value === undefined || value === null || value === '';
152
+ if (isEmpty && !rule.required)
153
+ continue;
154
+ if (rule.type) {
155
+ const typeError = checkType(value, rule);
156
+ if (typeError)
157
+ return typeError;
158
+ }
159
+ if (rule.min !== undefined && (typeof value === 'string' || Array.isArray(value))) {
160
+ if (value.length < rule.min)
161
+ return rule.message || `长度不能小于 ${rule.min}`;
162
+ }
163
+ if (rule.max !== undefined && (typeof value === 'string' || Array.isArray(value))) {
164
+ if (value.length > rule.max)
165
+ return rule.message || `长度不能大于 ${rule.max}`;
166
+ }
167
+ if (rule.len !== undefined && (typeof value === 'string' || Array.isArray(value))) {
168
+ if (value.length !== rule.len)
169
+ return rule.message || `长度必须为 ${rule.len}`;
170
+ }
171
+ if (rule.validator) {
172
+ console.warn('同步校验不支持异步 validator,请使用异步版本 validateSingleField');
173
+ const result = rule.validator(value, rule);
174
+ if (typeof result === 'string')
175
+ return result;
176
+ if (result === false)
177
+ return rule.message || '校验失败';
178
+ }
179
+ }
180
+ return undefined;
181
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fantasia-ui",
3
3
  "private": false,
4
- "version": "0.2.3",
4
+ "version": "0.2.4",
5
5
  "type": "module",
6
6
  "main": "dist/fantasia-ui.umd.js",
7
7
  "module": "dist/fantasia-ui.es.js",
@@ -12,7 +12,8 @@
12
12
  "import": "./dist/fantasia-ui.es.js",
13
13
  "require": "./dist/fantasia-ui.umd.js"
14
14
  },
15
- "./dist/index.css": "./dist/index.css"
15
+ "./index.css": "./dist/fantasia-ui.css",
16
+ "./*": "./dist/*"
16
17
  },
17
18
  "files": [
18
19
  "dist"
@@ -22,7 +23,7 @@
22
23
  },
23
24
  "author": "Shuster",
24
25
  "license": "MIT",
25
- "description": "一个基于 Vue 3 + TypeScript 的现代化 UI 组件库",
26
+ "description": "一个基于 Vue 3 + TypeScript 的轻量级现代化 UI 组件库",
26
27
  "keywords": [
27
28
  "vue3",
28
29
  "fantasia-ui",