@variojs/vue 0.0.1 → 0.0.2
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 +53 -848
- package/dist/bindings.js +1 -199
- package/dist/composable.js +1 -386
- package/dist/features/attrs-builder.js +1 -143
- package/dist/features/children-resolver.js +1 -171
- package/dist/features/component-resolver.js +1 -110
- package/dist/features/computed.js +1 -161
- package/dist/features/event-handler.js +1 -103
- package/dist/features/expression-evaluator.js +1 -28
- package/dist/features/lifecycle-wrapper.js +1 -102
- package/dist/features/loop-handler.js +1 -168
- package/dist/features/path-resolver.js +1 -171
- package/dist/features/provide-inject.js +1 -139
- package/dist/features/refs.js +1 -113
- package/dist/features/teleport.js +1 -32
- package/dist/features/validators.js +1 -297
- package/dist/features/watch.js +1 -154
- package/dist/index.js +1 -24
- package/dist/renderer.js +1 -244
- package/dist/types.js +0 -16
- package/package.json +18 -5
|
@@ -1,139 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Provide/Inject 支持
|
|
3
|
-
*
|
|
4
|
-
* 在 Schema 节点上支持 Vue 的依赖注入系统
|
|
5
|
-
*
|
|
6
|
-
* 特性:
|
|
7
|
-
* - provide: 向下传递数据给子组件
|
|
8
|
-
* - inject: 从父组件获取数据
|
|
9
|
-
* - 支持表达式求值:provide 的值可以是表达式
|
|
10
|
-
* - 支持默认值:inject 可以指定默认值
|
|
11
|
-
*/
|
|
12
|
-
import { provide, inject } from 'vue';
|
|
13
|
-
import { evaluate } from '@variojs/core';
|
|
14
|
-
/**
|
|
15
|
-
* 判断字符串是否看起来像一个表达式
|
|
16
|
-
* 表达式通常包含:变量名(无空格/引号)、点号访问、运算符等
|
|
17
|
-
*/
|
|
18
|
-
function looksLikeExpression(str) {
|
|
19
|
-
// 纯字母数字下划线的简单标识符可能是状态引用
|
|
20
|
-
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str)) {
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
// 包含点号访问:user.name
|
|
24
|
-
if (/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(str)) {
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
// 包含运算符的表达式
|
|
28
|
-
if (/[+\-*/%<>=!&|?:]/.test(str)) {
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* 处理 provide 配置
|
|
35
|
-
* 在组件 setup 中调用,向下传递数据
|
|
36
|
-
*
|
|
37
|
-
* @param schema Schema 节点
|
|
38
|
-
* @param ctx 运行时上下文
|
|
39
|
-
*/
|
|
40
|
-
export function setupProvide(schema, ctx) {
|
|
41
|
-
if (!schema.provide || Object.keys(schema.provide).length === 0) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
for (const [key, value] of Object.entries(schema.provide)) {
|
|
45
|
-
let resolvedValue;
|
|
46
|
-
// 如果值是字符串且看起来像表达式,尝试求值
|
|
47
|
-
if (typeof value === 'string' && looksLikeExpression(value)) {
|
|
48
|
-
try {
|
|
49
|
-
const result = evaluate(value, ctx);
|
|
50
|
-
// 只有当结果不是 undefined 时才使用求值结果
|
|
51
|
-
resolvedValue = result !== undefined ? result : value;
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// 表达式求值失败,使用原始字符串
|
|
55
|
-
resolvedValue = value;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
resolvedValue = value;
|
|
60
|
-
}
|
|
61
|
-
provide(key, resolvedValue);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* 规范化 inject 配置
|
|
66
|
-
*/
|
|
67
|
-
function normalizeInjectConfig(injectDef) {
|
|
68
|
-
if (!injectDef)
|
|
69
|
-
return [];
|
|
70
|
-
const configs = [];
|
|
71
|
-
if (Array.isArray(injectDef)) {
|
|
72
|
-
// 数组形式: ['theme', 'locale']
|
|
73
|
-
for (const key of injectDef) {
|
|
74
|
-
configs.push({
|
|
75
|
-
localKey: key,
|
|
76
|
-
from: key,
|
|
77
|
-
hasDefault: false
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
// 对象形式
|
|
83
|
-
for (const [localKey, config] of Object.entries(injectDef)) {
|
|
84
|
-
if (typeof config === 'string') {
|
|
85
|
-
// 简单映射: { myTheme: 'theme' }
|
|
86
|
-
configs.push({
|
|
87
|
-
localKey,
|
|
88
|
-
from: config,
|
|
89
|
-
hasDefault: false
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
// 完整配置: { myTheme: { from: 'theme', default: 'light' } }
|
|
94
|
-
configs.push({
|
|
95
|
-
localKey,
|
|
96
|
-
from: config.from || localKey,
|
|
97
|
-
defaultValue: config.default,
|
|
98
|
-
hasDefault: 'default' in config
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return configs;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* 处理 inject 配置
|
|
107
|
-
* 在组件 setup 中调用,从父组件获取数据
|
|
108
|
-
*
|
|
109
|
-
* @param schema Schema 节点
|
|
110
|
-
* @returns inject 的值对象,可合并到 props 或 attrs
|
|
111
|
-
*/
|
|
112
|
-
export function setupInject(schema) {
|
|
113
|
-
const configs = normalizeInjectConfig(schema.inject);
|
|
114
|
-
if (configs.length === 0) {
|
|
115
|
-
return {};
|
|
116
|
-
}
|
|
117
|
-
const result = {};
|
|
118
|
-
for (const config of configs) {
|
|
119
|
-
const { localKey, from, defaultValue, hasDefault } = config;
|
|
120
|
-
// 使用 Vue 的 inject 获取值
|
|
121
|
-
const value = hasDefault
|
|
122
|
-
? inject(from, defaultValue)
|
|
123
|
-
: inject(from);
|
|
124
|
-
result[localKey] = value;
|
|
125
|
-
}
|
|
126
|
-
return result;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* 在 setup 中同时处理 provide 和 inject
|
|
130
|
-
*
|
|
131
|
-
* @param schema Schema 节点
|
|
132
|
-
* @param ctx 运行时上下文
|
|
133
|
-
* @returns inject 的值对象
|
|
134
|
-
*/
|
|
135
|
-
export function setupProvideInject(schema, ctx) {
|
|
136
|
-
setupProvide(schema, ctx);
|
|
137
|
-
return setupInject(schema);
|
|
138
|
-
}
|
|
139
|
-
//# sourceMappingURL=provide-inject.js.map
|
|
1
|
+
import{provide as a,inject as u}from"vue";import{evaluate as c}from"@variojs/core";function p(e){return!!(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(e)||/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(e)||/[+\-*/%<>=!&|?:]/.test(e))}function d(e,r){if(!(!e.provide||Object.keys(e.provide).length===0))for(const[o,t]of Object.entries(e.provide)){let n;if(typeof t=="string"&&p(t))try{const f=c(t,r);n=f!==void 0?f:t}catch{n=t}else n=t;a(o,n)}}function y(e){if(!e)return[];const r=[];if(Array.isArray(e))for(const o of e)r.push({localKey:o,from:o,hasDefault:!1});else for(const[o,t]of Object.entries(e))typeof t=="string"?r.push({localKey:o,from:t,hasDefault:!1}):r.push({localKey:o,from:t.from||o,defaultValue:t.default,hasDefault:"default"in t});return r}function v(e){const r=y(e.inject);if(r.length===0)return{};const o={};for(const t of r){const{localKey:n,from:f,defaultValue:s,hasDefault:i}=t,l=i?u(f,s):u(f);o[n]=l}return o}function m(e,r){return d(e,r),v(e)}export{v as setupInject,d as setupProvide,m as setupProvideInject};
|
package/dist/features/refs.js
CHANGED
|
@@ -1,113 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Ref 支持
|
|
3
|
-
*
|
|
4
|
-
* 实现 Vue 的模板引用功能,允许在 Schema 中声明 ref,
|
|
5
|
-
* 并通过 useVario 返回的 refs 对象访问组件实例
|
|
6
|
-
*/
|
|
7
|
-
import { ref } from 'vue';
|
|
8
|
-
/**
|
|
9
|
-
* Refs 注册表
|
|
10
|
-
* 存储所有通过 ref 属性声明的组件引用
|
|
11
|
-
*
|
|
12
|
-
* 使用 Proxy 实现动态访问:用户可以通过 refs.xxx 访问任何 ref,
|
|
13
|
-
* 如果 ref 不存在会自动创建(懒加载)
|
|
14
|
-
*/
|
|
15
|
-
export class RefsRegistry {
|
|
16
|
-
refs = new Map();
|
|
17
|
-
_proxy = null;
|
|
18
|
-
/**
|
|
19
|
-
* 注册一个 ref
|
|
20
|
-
*/
|
|
21
|
-
register(name) {
|
|
22
|
-
if (!this.refs.has(name)) {
|
|
23
|
-
this.refs.set(name, ref(null));
|
|
24
|
-
}
|
|
25
|
-
return this.refs.get(name);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* 获取所有 refs(返回动态 Proxy,可以访问后续添加的 ref)
|
|
29
|
-
*/
|
|
30
|
-
getAll() {
|
|
31
|
-
if (!this._proxy) {
|
|
32
|
-
this._proxy = new Proxy({}, {
|
|
33
|
-
get: (_, prop) => {
|
|
34
|
-
// 自动注册并返回 ref
|
|
35
|
-
return this.register(prop);
|
|
36
|
-
},
|
|
37
|
-
ownKeys: () => {
|
|
38
|
-
return Array.from(this.refs.keys());
|
|
39
|
-
},
|
|
40
|
-
getOwnPropertyDescriptor: (_, prop) => {
|
|
41
|
-
if (this.refs.has(prop)) {
|
|
42
|
-
return { enumerable: true, configurable: true, value: this.refs.get(prop) };
|
|
43
|
-
}
|
|
44
|
-
return undefined;
|
|
45
|
-
},
|
|
46
|
-
has: (_, prop) => {
|
|
47
|
-
return this.refs.has(prop);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
return this._proxy;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* 获取指定的 ref
|
|
55
|
-
*/
|
|
56
|
-
get(name) {
|
|
57
|
-
return this.refs.get(name);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* 清除所有 refs(组件卸载时调用)
|
|
61
|
-
*/
|
|
62
|
-
clear() {
|
|
63
|
-
this.refs.clear();
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* 移除指定的 ref
|
|
67
|
-
*/
|
|
68
|
-
remove(name) {
|
|
69
|
-
return this.refs.delete(name);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* 为 VNode 添加 ref 处理
|
|
74
|
-
*/
|
|
75
|
-
export function attachRef(vnode, schema, refsRegistry) {
|
|
76
|
-
if (!schema.ref) {
|
|
77
|
-
return vnode;
|
|
78
|
-
}
|
|
79
|
-
const refValue = refsRegistry.register(schema.ref);
|
|
80
|
-
// 使用类型断言处理 ref(Vue 的 ref 类型比较复杂)
|
|
81
|
-
const vnodeAny = vnode;
|
|
82
|
-
// 如果 vnode 已经有 ref,需要合并处理
|
|
83
|
-
if (vnodeAny.ref) {
|
|
84
|
-
const existingRef = vnodeAny.ref;
|
|
85
|
-
const refCallback = (el) => {
|
|
86
|
-
// 调用原有的 ref
|
|
87
|
-
if (typeof existingRef === 'function') {
|
|
88
|
-
try {
|
|
89
|
-
existingRef(el);
|
|
90
|
-
}
|
|
91
|
-
catch (e) {
|
|
92
|
-
// 忽略错误
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
else if (existingRef && typeof existingRef === 'object' && 'value' in existingRef) {
|
|
96
|
-
try {
|
|
97
|
-
existingRef.value = el;
|
|
98
|
-
}
|
|
99
|
-
catch (e) {
|
|
100
|
-
// 忽略错误
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
// 设置到我们的 ref
|
|
104
|
-
refValue.value = el;
|
|
105
|
-
};
|
|
106
|
-
vnodeAny.ref = refCallback;
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
vnodeAny.ref = refValue;
|
|
110
|
-
}
|
|
111
|
-
return vnode;
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=refs.js.map
|
|
1
|
+
import{ref as h}from"vue";class l{refs=new Map;_proxy=null;register(e){return this.refs.has(e)||this.refs.set(e,h(null)),this.refs.get(e)}getAll(){return this._proxy||(this._proxy=new Proxy({},{get:(e,r)=>this.register(r),ownKeys:()=>Array.from(this.refs.keys()),getOwnPropertyDescriptor:(e,r)=>{if(this.refs.has(r))return{enumerable:!0,configurable:!0,value:this.refs.get(r)}},has:(e,r)=>this.refs.has(r)})),this._proxy}get(e){return this.refs.get(e)}clear(){this.refs.clear()}remove(e){return this.refs.delete(e)}}function c(s,e,r){if(!e.ref)return s;const n=r.register(e.ref),f=s;if(f.ref){const t=f.ref,u=i=>{if(typeof t=="function")try{t(i)}catch{}else if(t&&typeof t=="object"&&"value"in t)try{t.value=i}catch{}n.value=i};f.ref=u}else f.ref=n;return s}export{l as RefsRegistry,c as attachRef};
|
|
@@ -1,32 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Teleport 支持
|
|
3
|
-
*
|
|
4
|
-
* 将组件传送到指定的 DOM 节点
|
|
5
|
-
*
|
|
6
|
-
* 特性:
|
|
7
|
-
* - 支持 CSS 选择器目标
|
|
8
|
-
* - 支持 boolean(true = body)
|
|
9
|
-
* - 优雅地处理空 children
|
|
10
|
-
*/
|
|
11
|
-
import { h, Teleport } from 'vue';
|
|
12
|
-
/**
|
|
13
|
-
* 创建 Teleport VNode
|
|
14
|
-
*
|
|
15
|
-
* @param target 传送目标(CSS 选择器或 true 表示 body)
|
|
16
|
-
* @param children 子节点
|
|
17
|
-
*/
|
|
18
|
-
export function createTeleport(target, children) {
|
|
19
|
-
const to = target === true ? 'body' : target;
|
|
20
|
-
const normalizedChildren = children
|
|
21
|
-
? (Array.isArray(children) ? children : [children])
|
|
22
|
-
: [];
|
|
23
|
-
// Teleport 需要使用 as any 来绑定类型
|
|
24
|
-
return h(Teleport, { to }, normalizedChildren);
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* 检查是否需要 teleport
|
|
28
|
-
*/
|
|
29
|
-
export function shouldTeleport(target) {
|
|
30
|
-
return target !== undefined && target !== false;
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=teleport.js.map
|
|
1
|
+
import{h as n,Teleport as u}from"vue";function f(o,r){const e=o===!0?"body":o,t=r?Array.isArray(r)?r:[r]:[];return n(u,{to:e},t)}function s(o){return o!==void 0&&o!==!1}export{f as createTeleport,s as shouldTeleport};
|
|
@@ -1,297 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* 内置验证器模块
|
|
3
|
-
*
|
|
4
|
-
* 提供常用的表单验证规则:
|
|
5
|
-
* - 必填验证
|
|
6
|
-
* - 格式验证(邮箱、URL、手机号等)
|
|
7
|
-
* - 长度验证
|
|
8
|
-
* - 数值范围验证
|
|
9
|
-
* - 正则匹配
|
|
10
|
-
* - 自定义验证
|
|
11
|
-
*/
|
|
12
|
-
/**
|
|
13
|
-
* 默认错误消息
|
|
14
|
-
*/
|
|
15
|
-
const DEFAULT_MESSAGES = {
|
|
16
|
-
required: '此字段为必填项',
|
|
17
|
-
email: '请输入有效的邮箱地址',
|
|
18
|
-
url: '请输入有效的 URL',
|
|
19
|
-
phone: '请输入有效的手机号',
|
|
20
|
-
number: '请输入数字',
|
|
21
|
-
integer: '请输入整数',
|
|
22
|
-
alpha: '只能包含字母',
|
|
23
|
-
alphanumeric: '只能包含字母和数字',
|
|
24
|
-
min: '值不能小于 {value}',
|
|
25
|
-
max: '值不能大于 {value}',
|
|
26
|
-
minLength: '长度不能少于 {value} 个字符',
|
|
27
|
-
maxLength: '长度不能超过 {value} 个字符',
|
|
28
|
-
pattern: '格式不正确'
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* 验证正则表达式
|
|
32
|
-
*/
|
|
33
|
-
const PATTERNS = {
|
|
34
|
-
email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
35
|
-
url: /^https?:\/\/[^\s]+$/,
|
|
36
|
-
phone: /^1[3-9]\d{9}$/,
|
|
37
|
-
number: /^-?\d*\.?\d+$/,
|
|
38
|
-
integer: /^-?\d+$/,
|
|
39
|
-
alpha: /^[a-zA-Z]+$/,
|
|
40
|
-
alphanumeric: /^[a-zA-Z0-9]+$/
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* 检查值是否为空
|
|
44
|
-
*/
|
|
45
|
-
const isEmpty = (value) => {
|
|
46
|
-
if (value === null || value === undefined)
|
|
47
|
-
return true;
|
|
48
|
-
if (typeof value === 'string')
|
|
49
|
-
return value.trim() === '';
|
|
50
|
-
if (Array.isArray(value))
|
|
51
|
-
return value.length === 0;
|
|
52
|
-
return false;
|
|
53
|
-
};
|
|
54
|
-
/**
|
|
55
|
-
* 内置验证器
|
|
56
|
-
*/
|
|
57
|
-
const builtinValidators = {
|
|
58
|
-
required: (value) => ({
|
|
59
|
-
valid: !isEmpty(value),
|
|
60
|
-
message: DEFAULT_MESSAGES.required
|
|
61
|
-
}),
|
|
62
|
-
email: (value) => {
|
|
63
|
-
if (isEmpty(value))
|
|
64
|
-
return { valid: true };
|
|
65
|
-
return {
|
|
66
|
-
valid: PATTERNS.email.test(String(value)),
|
|
67
|
-
message: DEFAULT_MESSAGES.email
|
|
68
|
-
};
|
|
69
|
-
},
|
|
70
|
-
url: (value) => {
|
|
71
|
-
if (isEmpty(value))
|
|
72
|
-
return { valid: true };
|
|
73
|
-
return {
|
|
74
|
-
valid: PATTERNS.url.test(String(value)),
|
|
75
|
-
message: DEFAULT_MESSAGES.url
|
|
76
|
-
};
|
|
77
|
-
},
|
|
78
|
-
phone: (value) => {
|
|
79
|
-
if (isEmpty(value))
|
|
80
|
-
return { valid: true };
|
|
81
|
-
return {
|
|
82
|
-
valid: PATTERNS.phone.test(String(value)),
|
|
83
|
-
message: DEFAULT_MESSAGES.phone
|
|
84
|
-
};
|
|
85
|
-
},
|
|
86
|
-
number: (value) => {
|
|
87
|
-
if (isEmpty(value))
|
|
88
|
-
return { valid: true };
|
|
89
|
-
return {
|
|
90
|
-
valid: PATTERNS.number.test(String(value)),
|
|
91
|
-
message: DEFAULT_MESSAGES.number
|
|
92
|
-
};
|
|
93
|
-
},
|
|
94
|
-
integer: (value) => {
|
|
95
|
-
if (isEmpty(value))
|
|
96
|
-
return { valid: true };
|
|
97
|
-
return {
|
|
98
|
-
valid: PATTERNS.integer.test(String(value)),
|
|
99
|
-
message: DEFAULT_MESSAGES.integer
|
|
100
|
-
};
|
|
101
|
-
},
|
|
102
|
-
alpha: (value) => {
|
|
103
|
-
if (isEmpty(value))
|
|
104
|
-
return { valid: true };
|
|
105
|
-
return {
|
|
106
|
-
valid: PATTERNS.alpha.test(String(value)),
|
|
107
|
-
message: DEFAULT_MESSAGES.alpha
|
|
108
|
-
};
|
|
109
|
-
},
|
|
110
|
-
alphanumeric: (value) => {
|
|
111
|
-
if (isEmpty(value))
|
|
112
|
-
return { valid: true };
|
|
113
|
-
return {
|
|
114
|
-
valid: PATTERNS.alphanumeric.test(String(value)),
|
|
115
|
-
message: DEFAULT_MESSAGES.alphanumeric
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
/**
|
|
120
|
-
* 参数化验证器
|
|
121
|
-
*/
|
|
122
|
-
const parameterizedValidators = {
|
|
123
|
-
min: (value, param) => {
|
|
124
|
-
if (isEmpty(value))
|
|
125
|
-
return { valid: true };
|
|
126
|
-
const num = Number(value);
|
|
127
|
-
const min = Number(param);
|
|
128
|
-
return {
|
|
129
|
-
valid: !isNaN(num) && num >= min,
|
|
130
|
-
message: DEFAULT_MESSAGES.min.replace('{value}', String(param))
|
|
131
|
-
};
|
|
132
|
-
},
|
|
133
|
-
max: (value, param) => {
|
|
134
|
-
if (isEmpty(value))
|
|
135
|
-
return { valid: true };
|
|
136
|
-
const num = Number(value);
|
|
137
|
-
const max = Number(param);
|
|
138
|
-
return {
|
|
139
|
-
valid: !isNaN(num) && num <= max,
|
|
140
|
-
message: DEFAULT_MESSAGES.max.replace('{value}', String(param))
|
|
141
|
-
};
|
|
142
|
-
},
|
|
143
|
-
minLength: (value, param) => {
|
|
144
|
-
if (isEmpty(value))
|
|
145
|
-
return { valid: true };
|
|
146
|
-
const len = String(value).length;
|
|
147
|
-
const minLen = Number(param);
|
|
148
|
-
return {
|
|
149
|
-
valid: len >= minLen,
|
|
150
|
-
message: DEFAULT_MESSAGES.minLength.replace('{value}', String(param))
|
|
151
|
-
};
|
|
152
|
-
},
|
|
153
|
-
maxLength: (value, param) => {
|
|
154
|
-
if (isEmpty(value))
|
|
155
|
-
return { valid: true };
|
|
156
|
-
const len = String(value).length;
|
|
157
|
-
const maxLen = Number(param);
|
|
158
|
-
return {
|
|
159
|
-
valid: len <= maxLen,
|
|
160
|
-
message: DEFAULT_MESSAGES.maxLength.replace('{value}', String(param))
|
|
161
|
-
};
|
|
162
|
-
},
|
|
163
|
-
pattern: (value, param) => {
|
|
164
|
-
if (isEmpty(value))
|
|
165
|
-
return { valid: true };
|
|
166
|
-
const regex = param instanceof RegExp ? param : new RegExp(String(param));
|
|
167
|
-
return {
|
|
168
|
-
valid: regex.test(String(value)),
|
|
169
|
-
message: DEFAULT_MESSAGES.pattern
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
/**
|
|
174
|
-
* 执行单个验证规则
|
|
175
|
-
*/
|
|
176
|
-
export const executeRule = async (value, rule, ctx) => {
|
|
177
|
-
// 字符串规则(内置规则简写)
|
|
178
|
-
if (typeof rule === 'string') {
|
|
179
|
-
const validator = builtinValidators[rule];
|
|
180
|
-
if (validator) {
|
|
181
|
-
return validator(value);
|
|
182
|
-
}
|
|
183
|
-
return { valid: true };
|
|
184
|
-
}
|
|
185
|
-
// 函数规则(自定义验证器)
|
|
186
|
-
if (typeof rule === 'function') {
|
|
187
|
-
try {
|
|
188
|
-
const result = await rule(value, ctx);
|
|
189
|
-
if (typeof result === 'boolean') {
|
|
190
|
-
return { valid: result, message: result ? undefined : '验证失败' };
|
|
191
|
-
}
|
|
192
|
-
return { valid: false, message: result };
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
return { valid: false, message: error instanceof Error ? error.message : '验证出错' };
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
// 参数化规则
|
|
199
|
-
if (typeof rule === 'object' && 'type' in rule) {
|
|
200
|
-
const paramRule = rule;
|
|
201
|
-
// 自定义规则
|
|
202
|
-
if (paramRule.type === 'custom' && typeof paramRule.value === 'function') {
|
|
203
|
-
try {
|
|
204
|
-
const result = await paramRule.value(value, ctx);
|
|
205
|
-
if (typeof result === 'boolean') {
|
|
206
|
-
return { valid: result, message: result ? undefined : paramRule.message || '验证失败' };
|
|
207
|
-
}
|
|
208
|
-
return { valid: false, message: result };
|
|
209
|
-
}
|
|
210
|
-
catch (error) {
|
|
211
|
-
return { valid: false, message: error instanceof Error ? error.message : '验证出错' };
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
// 其他参数化规则
|
|
215
|
-
const validator = parameterizedValidators[paramRule.type];
|
|
216
|
-
if (validator) {
|
|
217
|
-
const result = validator(value, paramRule.value);
|
|
218
|
-
return {
|
|
219
|
-
valid: result.valid,
|
|
220
|
-
message: paramRule.message || result.message
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return { valid: true };
|
|
225
|
-
};
|
|
226
|
-
/**
|
|
227
|
-
* 规范化验证配置
|
|
228
|
-
*/
|
|
229
|
-
export const normalizeValidateConfig = (config) => {
|
|
230
|
-
// 单个规则
|
|
231
|
-
if (typeof config === 'string' || typeof config === 'function') {
|
|
232
|
-
return {
|
|
233
|
-
rules: [config],
|
|
234
|
-
trigger: ['change']
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
// 规则数组
|
|
238
|
-
if (Array.isArray(config)) {
|
|
239
|
-
return {
|
|
240
|
-
rules: config,
|
|
241
|
-
trigger: ['change']
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
// 完整配置对象
|
|
245
|
-
if (typeof config === 'object' && 'type' in config) {
|
|
246
|
-
// ParameterizedValidateRule
|
|
247
|
-
return {
|
|
248
|
-
rules: [config],
|
|
249
|
-
trigger: ['change']
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
// 配置对象
|
|
253
|
-
const fullConfig = config;
|
|
254
|
-
return {
|
|
255
|
-
rules: fullConfig.rules,
|
|
256
|
-
trigger: Array.isArray(fullConfig.trigger)
|
|
257
|
-
? fullConfig.trigger
|
|
258
|
-
: [fullConfig.trigger || 'change'],
|
|
259
|
-
message: fullConfig.message,
|
|
260
|
-
errorPath: fullConfig.errorPath
|
|
261
|
-
};
|
|
262
|
-
};
|
|
263
|
-
/**
|
|
264
|
-
* 执行完整验证
|
|
265
|
-
*/
|
|
266
|
-
export const validate = async (value, config, ctx) => {
|
|
267
|
-
const { rules, message: globalMessage } = normalizeValidateConfig(config);
|
|
268
|
-
for (const rule of rules) {
|
|
269
|
-
const result = await executeRule(value, rule, ctx);
|
|
270
|
-
if (!result.valid) {
|
|
271
|
-
return {
|
|
272
|
-
valid: false,
|
|
273
|
-
message: globalMessage || result.message
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return { valid: true };
|
|
278
|
-
};
|
|
279
|
-
/**
|
|
280
|
-
* 创建验证处理器(用于绑定到事件)
|
|
281
|
-
*/
|
|
282
|
-
export const createValidationHandler = (modelPath, config, setError, ctx) => {
|
|
283
|
-
const { rules, message: globalMessage, errorPath } = normalizeValidateConfig(config);
|
|
284
|
-
const errPath = errorPath || `${modelPath}Error`;
|
|
285
|
-
return async (value) => {
|
|
286
|
-
for (const rule of rules) {
|
|
287
|
-
const result = await executeRule(value, rule, ctx);
|
|
288
|
-
if (!result.valid) {
|
|
289
|
-
setError(errPath, globalMessage || result.message || '验证失败');
|
|
290
|
-
return false;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
setError(errPath, null);
|
|
294
|
-
return true;
|
|
295
|
-
};
|
|
296
|
-
};
|
|
297
|
-
//# sourceMappingURL=validators.js.map
|
|
1
|
+
const i={required:"\u6B64\u5B57\u6BB5\u4E3A\u5FC5\u586B\u9879",email:"\u8BF7\u8F93\u5165\u6709\u6548\u7684\u90AE\u7BB1\u5730\u5740",url:"\u8BF7\u8F93\u5165\u6709\u6548\u7684 URL",phone:"\u8BF7\u8F93\u5165\u6709\u6548\u7684\u624B\u673A\u53F7",number:"\u8BF7\u8F93\u5165\u6570\u5B57",integer:"\u8BF7\u8F93\u5165\u6574\u6570",alpha:"\u53EA\u80FD\u5305\u542B\u5B57\u6BCD",alphanumeric:"\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u548C\u6570\u5B57",min:"\u503C\u4E0D\u80FD\u5C0F\u4E8E {value}",max:"\u503C\u4E0D\u80FD\u5927\u4E8E {value}",minLength:"\u957F\u5EA6\u4E0D\u80FD\u5C11\u4E8E {value} \u4E2A\u5B57\u7B26",maxLength:"\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7 {value} \u4E2A\u5B57\u7B26",pattern:"\u683C\u5F0F\u4E0D\u6B63\u786E"},s={email:/^[^\s@]+@[^\s@]+\.[^\s@]+$/,url:/^https?:\/\/[^\s]+$/,phone:/^1[3-9]\d{9}$/,number:/^-?\d*\.?\d+$/,integer:/^-?\d+$/,alpha:/^[a-zA-Z]+$/,alphanumeric:/^[a-zA-Z0-9]+$/},n=e=>e==null?!0:typeof e=="string"?e.trim()==="":Array.isArray(e)?e.length===0:!1,f={required:e=>({valid:!n(e),message:i.required}),email:e=>n(e)?{valid:!0}:{valid:s.email.test(String(e)),message:i.email},url:e=>n(e)?{valid:!0}:{valid:s.url.test(String(e)),message:i.url},phone:e=>n(e)?{valid:!0}:{valid:s.phone.test(String(e)),message:i.phone},number:e=>n(e)?{valid:!0}:{valid:s.number.test(String(e)),message:i.number},integer:e=>n(e)?{valid:!0}:{valid:s.integer.test(String(e)),message:i.integer},alpha:e=>n(e)?{valid:!0}:{valid:s.alpha.test(String(e)),message:i.alpha},alphanumeric:e=>n(e)?{valid:!0}:{valid:s.alphanumeric.test(String(e)),message:i.alphanumeric}},h={min:(e,u)=>{if(n(e))return{valid:!0};const a=Number(e),r=Number(u);return{valid:!isNaN(a)&&a>=r,message:i.min.replace("{value}",String(u))}},max:(e,u)=>{if(n(e))return{valid:!0};const a=Number(e),r=Number(u);return{valid:!isNaN(a)&&a<=r,message:i.max.replace("{value}",String(u))}},minLength:(e,u)=>{if(n(e))return{valid:!0};const a=String(e).length,r=Number(u);return{valid:a>=r,message:i.minLength.replace("{value}",String(u))}},maxLength:(e,u)=>{if(n(e))return{valid:!0};const a=String(e).length,r=Number(u);return{valid:a<=r,message:i.maxLength.replace("{value}",String(u))}},pattern:(e,u)=>n(e)?{valid:!0}:{valid:(u instanceof RegExp?u:new RegExp(String(u))).test(String(e)),message:i.pattern}},m=async(e,u,a)=>{if(typeof u=="string"){const r=f[u];return r?r(e):{valid:!0}}if(typeof u=="function")try{const r=await u(e,a);return typeof r=="boolean"?{valid:r,message:r?void 0:"\u9A8C\u8BC1\u5931\u8D25"}:{valid:!1,message:r}}catch(r){return{valid:!1,message:r instanceof Error?r.message:"\u9A8C\u8BC1\u51FA\u9519"}}if(typeof u=="object"&&"type"in u){const r=u;if(r.type==="custom"&&typeof r.value=="function")try{const t=await r.value(e,a);return typeof t=="boolean"?{valid:t,message:t?void 0:r.message||"\u9A8C\u8BC1\u5931\u8D25"}:{valid:!1,message:t}}catch(t){return{valid:!1,message:t instanceof Error?t.message:"\u9A8C\u8BC1\u51FA\u9519"}}const l=h[r.type];if(l){const t=l(e,r.value);return{valid:t.valid,message:r.message||t.message}}}return{valid:!0}},o=e=>{if(typeof e=="string"||typeof e=="function")return{rules:[e],trigger:["change"]};if(Array.isArray(e))return{rules:e,trigger:["change"]};if(typeof e=="object"&&"type"in e)return{rules:[e],trigger:["change"]};const u=e;return{rules:u.rules,trigger:Array.isArray(u.trigger)?u.trigger:[u.trigger||"change"],message:u.message,errorPath:u.errorPath}},B=async(e,u,a)=>{const{rules:r,message:l}=o(u);for(const t of r){const g=await m(e,t,a);if(!g.valid)return{valid:!1,message:l||g.message}}return{valid:!0}},y=(e,u,a,r)=>{const{rules:l,message:t,errorPath:g}=o(u),v=g||`${e}Error`;return async c=>{for(const p of l){const d=await m(c,p,r);if(!d.valid)return a(v,t||d.message||"\u9A8C\u8BC1\u5931\u8D25"),!1}return a(v,null),!0}};export{y as createValidationHandler,m as executeRule,o as normalizeValidateConfig,B as validate};
|