fix(web): 修复 visible_when 表达式评估器 !=/||/&& 支持 + 添加 validation 前端校验

- exprEvaluator: 新增 neq 类型修复 != 操作符被当作 == 处理的 bug
- exprEvaluator: 支持 || 和 && 作为 OR/AND 的别名
- PluginCRUDPage: 读取 field.validation.pattern 添加表单正则校验规则
This commit is contained in:
iven
2026-04-21 00:19:10 +08:00
parent 89fc482d99
commit 5ac8e18d74
2 changed files with 24 additions and 7 deletions

View File

@@ -773,9 +773,14 @@ export default function PluginCRUDPage({
name={field.name}
label={field.display_name || field.name}
rules={
field.required
? [{ required: true, message: `请输入${field.display_name || field.name}` }]
: []
[
...(field.required
? [{ required: true, message: `请输入${field.display_name || field.name}` }]
: []),
...(field.validation?.pattern
? [{ pattern: new RegExp(field.validation.pattern), message: field.validation.message || '格式不正确' }]
: []),
]
}
valuePropName={field.field_type === 'boolean' ? 'checked' : 'value'}
>

View File

@@ -11,7 +11,7 @@
*/
interface ExprNode {
type: 'eq' | 'and' | 'or' | 'not';
type: 'eq' | 'neq' | 'and' | 'or' | 'not';
field?: string;
value?: string;
left?: ExprNode;
@@ -49,6 +49,16 @@ function tokenize(input: string): string[] {
i += 2;
continue;
}
if (input[i] === '&' && input[i + 1] === '&') {
tokens.push('&&');
i += 2;
continue;
}
if (input[i] === '|' && input[i + 1] === '|') {
tokens.push('||');
i += 2;
continue;
}
let j = i;
while (
j < input.length &&
@@ -81,12 +91,12 @@ function parseAtom(tokens: string[]): ExprNode | null {
if (op !== '==' && op !== '!=') return null;
const rawValue = tokens.shift() || '';
const value = rawValue.replace(/^'(.*)'$/, '$1');
return { type: 'eq', field, value };
return { type: op === '!=' ? 'neq' : 'eq', field, value };
}
function parseAnd(tokens: string[]): ExprNode | null {
let left = parseAtom(tokens);
while (tokens[0] === 'AND') {
while (tokens[0] === 'AND' || tokens[0] === '&&') {
tokens.shift();
const right = parseAtom(tokens);
if (left && right) {
@@ -98,7 +108,7 @@ function parseAnd(tokens: string[]): ExprNode | null {
function parseOr(tokens: string[]): ExprNode | null {
let left = parseAnd(tokens);
while (tokens[0] === 'OR') {
while (tokens[0] === 'OR' || tokens[0] === '||') {
tokens.shift();
const right = parseAnd(tokens);
if (left && right) {
@@ -117,6 +127,8 @@ export function evaluateExpr(node: ExprNode, values: Record<string, unknown>): b
switch (node.type) {
case 'eq':
return String(values[node.field!] ?? '') === node.value;
case 'neq':
return String(values[node.field!] ?? '') !== node.value;
case 'and':
return evaluateExpr(node.left!, values) && evaluateExpr(node.right!, values);
case 'or':