fix(web): 修复 visible_when 表达式评估器 !=/||/&& 支持 + 添加 validation 前端校验
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

- 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} name={field.name}
label={field.display_name || field.name} label={field.display_name || field.name}
rules={ 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'} valuePropName={field.field_type === 'boolean' ? 'checked' : 'value'}
> >

View File

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