mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
feat: variable supported helpers
This commit is contained in:
parent
dd2c1b64c6
commit
22a9f2bd6e
158
docs/zh-CN/developer/filter-operators/variable-filter-mapping.md
Normal file
158
docs/zh-CN/developer/filter-operators/variable-filter-mapping.md
Normal file
@ -0,0 +1,158 @@
|
||||
# VariableFilterMapping 使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
VariableFilterMapping 是 NocoBase 中一个用于处理变量助手条件映射的工具类。它允许开发者定义变量与助手条件之间的映射关系,使系统能够根据变量值动态生成数据查询条件。
|
||||
|
||||
## 主要功能
|
||||
|
||||
- 将变量值转换为数据查询条件
|
||||
- 支持多种助手操作符
|
||||
- 处理不同类型的变量(字符串、数字、日期等)
|
||||
- 支持自定义变量处理器
|
||||
|
||||
## 基本用法
|
||||
|
||||
### 1. 定义助手条件映射
|
||||
|
||||
```typescript
|
||||
import { VariableFilterMapping } from '@nocobase/database';
|
||||
|
||||
const helperMapping = new VariableFilterMapping({
|
||||
'user.id': '$user.id', // 映射用户ID
|
||||
'user.name': '$user.name', // 映射用户名
|
||||
'created_at': '$dateRange', // 映射日期范围
|
||||
'status': '$status' // 映射状态
|
||||
});
|
||||
```
|
||||
|
||||
### 2. 使用映射生成助手条件
|
||||
|
||||
```typescript
|
||||
// 变量值
|
||||
const variables = {
|
||||
'$user.id': 1,
|
||||
'$user.name': 'admin',
|
||||
'$dateRange': ['2023-01-01', '2023-12-31'],
|
||||
'$status': 'active'
|
||||
};
|
||||
|
||||
// 生成助手条件
|
||||
const helper = helperMapping.toHelper(variables);
|
||||
```
|
||||
|
||||
生成的助手条件类似于:
|
||||
|
||||
```javascript
|
||||
{
|
||||
'user.id': 1,
|
||||
'user.name': 'admin',
|
||||
'created_at': {
|
||||
$between: ['2023-01-01', '2023-12-31']
|
||||
},
|
||||
'status': 'active'
|
||||
}
|
||||
```
|
||||
|
||||
## 高级用法
|
||||
|
||||
### 1. 自定义操作符
|
||||
|
||||
```typescript
|
||||
const helperMapping = new VariableFilterMapping({
|
||||
'price': {
|
||||
name: '$price',
|
||||
operator: '$gt' // 使用大于操作符
|
||||
}
|
||||
});
|
||||
|
||||
const helper = helperMapping.toHelper({
|
||||
'$price': 100
|
||||
});
|
||||
|
||||
// 生成: { price: { $gt: 100 } }
|
||||
```
|
||||
|
||||
### 2. 嵌套映射
|
||||
|
||||
```typescript
|
||||
const helperMapping = new VariableFilterMapping({
|
||||
'product': {
|
||||
'price': {
|
||||
name: '$product.price',
|
||||
operator: '$between'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const helper = helperMapping.toHelper({
|
||||
'$product.price': [100, 200]
|
||||
});
|
||||
|
||||
// 生成: { product: { price: { $between: [100, 200] } } }
|
||||
```
|
||||
|
||||
### 3. 使用处理器
|
||||
|
||||
```typescript
|
||||
const helperMapping = new VariableFilterMapping({
|
||||
'tags': {
|
||||
name: '$tags',
|
||||
operator: '$match',
|
||||
processor: (value) => {
|
||||
// 将逗号分隔的标签转换为数组
|
||||
return value.split(',');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const helper = helperMapping.toHelper({
|
||||
'$tags': 'javascript,typescript,react'
|
||||
});
|
||||
|
||||
// 生成: { tags: { $match: ['javascript', 'typescript', 'react'] } }
|
||||
```
|
||||
|
||||
## 实际应用场景
|
||||
|
||||
1. **数据表格筛选**:根据用户界面中的筛选选项动态生成查询条件
|
||||
2. **权限控制**:基于当前用户信息生成数据访问限制
|
||||
3. **报表生成**:使用用户选择的参数动态生成报表数据查询条件
|
||||
4. **工作流触发条件**:定义基于变量的工作流触发条件
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 变量名必须以 `$` 开头
|
||||
- 确保变量值与期望的操作符兼容(例如,$between 操作符需要数组值)
|
||||
- 注意处理变量值为 null 或 undefined 的情况
|
||||
- 复杂的助手逻辑可能需要使用处理器函数进行转换
|
||||
|
||||
## API 参考
|
||||
|
||||
### VariableFilterMapping 构造函数
|
||||
|
||||
```typescript
|
||||
constructor(options: Record<string, MappingOptions | string>)
|
||||
```
|
||||
|
||||
### MappingOptions 接口
|
||||
|
||||
```typescript
|
||||
interface MappingOptions {
|
||||
name: string; // 变量名
|
||||
operator?: string; // 操作符名称
|
||||
processor?: (value) => any; // 值处理器
|
||||
}
|
||||
```
|
||||
|
||||
### toHelper 方法
|
||||
|
||||
```typescript
|
||||
toHelper(variables: Record<string, any>): Record<string, any>
|
||||
```
|
||||
|
||||
将变量值转换为助手条件。
|
||||
|
||||
## 总结
|
||||
|
||||
VariableFilterMapping 是 NocoBase 中强大的查询条件生成工具,它通过定义变量与助手条件的映射关系,使应用能够基于动态变量生成灵活的数据查询条件。掌握这一工具可以帮助开发者更有效地实现动态数据筛选、权限控制和自定义业务逻辑。
|
@ -19,6 +19,48 @@ import { useApp } from '../../../../application';
|
||||
import { SchemaComponent } from '../../../core/SchemaComponent';
|
||||
import { useVariable } from '../VariableProvider';
|
||||
import { helpersObs, rawHelpersObs, removeHelper } from './observables';
|
||||
import { VariableHelperMapping } from '../Input';
|
||||
import minimatch from 'minimatch';
|
||||
|
||||
/**
|
||||
* Escapes special glob characters in a string
|
||||
* @param str The string to escape
|
||||
* @returns The escaped string
|
||||
*/
|
||||
function escapeGlob(str: string): string {
|
||||
return str.replace(/[?*[\](){}!|+@\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a filter is allowed for a given variable based on the variableHelperMapping configuration
|
||||
* @param variableName The name of the variable to test
|
||||
* @param filterName The name of the filter to test
|
||||
* @param mapping The variable helper mapping configuration
|
||||
* @returns boolean indicating if the filter is allowed for the variable
|
||||
*/
|
||||
export function isFilterAllowedForVariable(
|
||||
variableName: string,
|
||||
filterName: string,
|
||||
mapping?: VariableHelperMapping,
|
||||
): boolean {
|
||||
if (!mapping?.rules) {
|
||||
return true; // If no rules defined, allow all filters
|
||||
}
|
||||
|
||||
// Check each rule
|
||||
for (const rule of mapping.rules) {
|
||||
// Check if variable matches the pattern
|
||||
// We don't escape the pattern since it's meant to be a glob pattern
|
||||
// But we escape the variable name since it's a literal value
|
||||
if (minimatch(escapeGlob(variableName), rule.variables)) {
|
||||
// Check if filter matches any of the allowed patterns
|
||||
return rule.filters.some((pattern) => minimatch(filterName, pattern));
|
||||
}
|
||||
}
|
||||
|
||||
// If no matching rule found and strictMode is true, deny the filter
|
||||
return !mapping.strictMode;
|
||||
}
|
||||
|
||||
export const HelperConfiguator = observer(
|
||||
({ index, close }: { index: number; close: () => void }) => {
|
||||
|
@ -12,7 +12,6 @@ import { css, cx } from '@emotion/css';
|
||||
import { autorun } from '@formily/reactive';
|
||||
import { useForm, observer } from '@formily/react';
|
||||
import { error } from '@nocobase/utils/client';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { extractTemplateElements, composeTemplate } from '@nocobase/json-template-parser';
|
||||
import {
|
||||
Input as AntInput,
|
||||
@ -46,6 +45,23 @@ type ParseOptions = {
|
||||
stringToDate?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration for mapping variables to their allowed filter functions
|
||||
*/
|
||||
interface VariableHelperRule {
|
||||
/** Pattern to match variables, supports glob patterns */
|
||||
variables: string;
|
||||
/** Array of allowed filter patterns, supports glob patterns */
|
||||
filters: string[];
|
||||
}
|
||||
|
||||
interface VariableHelperMapping {
|
||||
/** Array of rules defining which filters are allowed for which variables */
|
||||
rules: VariableHelperRule[];
|
||||
/** Optional flag to determine if unlisted combinations should be allowed */
|
||||
strictMode?: boolean;
|
||||
}
|
||||
|
||||
function parseValue(value: any, options: ParseOptions = {}): string | string[] {
|
||||
if (value == null || (Array.isArray(value) && value.length === 0)) {
|
||||
return 'null';
|
||||
@ -200,6 +216,7 @@ export type VariableInputProps = {
|
||||
className?: string;
|
||||
parseOptions?: ParseOptions;
|
||||
hideVariableButton?: boolean;
|
||||
variableHelperMapping?: VariableHelperMapping;
|
||||
};
|
||||
|
||||
export function Input(props: VariableInputProps) {
|
||||
|
@ -14,7 +14,7 @@ interface VariableContextValue {
|
||||
value: any;
|
||||
}
|
||||
|
||||
interface eProviderProps {
|
||||
interface VariableProviderProps {
|
||||
variableName: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
@ -25,6 +25,23 @@ import { useCurrentUserVariable } from './hooks/useUserVariable';
|
||||
import { useVariableOptions } from './hooks/useVariableOptions';
|
||||
import { Option } from './type';
|
||||
|
||||
/**
|
||||
* Configuration for mapping variables to their allowed filter functions
|
||||
*/
|
||||
interface VariableFilterRule {
|
||||
/** Pattern to match variables, supports glob patterns */
|
||||
variables: string;
|
||||
/** Array of allowed filter patterns, supports glob patterns */
|
||||
filters: string[];
|
||||
}
|
||||
|
||||
interface VariableFilterMapping {
|
||||
/** Array of rules defining which filters are allowed for which variables */
|
||||
rules: VariableFilterRule[];
|
||||
/** Optional flag to determine if unlisted combinations should be allowed */
|
||||
strictMode?: boolean;
|
||||
}
|
||||
|
||||
interface GetShouldChangeProps {
|
||||
collectionField: CollectionFieldOptions_deprecated;
|
||||
variables: VariablesContextType;
|
||||
@ -43,6 +60,8 @@ type Props = {
|
||||
onChange: (value: any, optionPath?: any[]) => void;
|
||||
renderSchemaComponent: (props: RenderSchemaComponentProps) => any;
|
||||
schema?: any;
|
||||
/** Configuration for mapping variables to their allowed filter functions */
|
||||
variableFilterMapping?: VariableFilterMapping;
|
||||
/** 消费变量值的字段 */
|
||||
targetFieldSchema?: Schema;
|
||||
children?: any;
|
||||
@ -344,3 +363,37 @@ export function useCompatOldVariables(props: {
|
||||
|
||||
return { compatOldVariables };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a variable and filter combination is allowed according to the mapping rules
|
||||
*/
|
||||
function isFilterAllowedForVariable(
|
||||
variable: string,
|
||||
filter: string,
|
||||
rules: VariableFilterRule[],
|
||||
strictMode = false,
|
||||
): boolean {
|
||||
// If no rules defined and not in strict mode, allow everything
|
||||
if (!rules?.length && !strictMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const rule of rules) {
|
||||
const variablePattern = new RegExp('^' + rule.variables.replace(/\*/g, '.*') + '$');
|
||||
if (variablePattern.test(variable)) {
|
||||
// If no filters defined for this rule, allow all helpers
|
||||
if (!rule.filters?.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if any of the filter patterns match the helper
|
||||
return rule.filters.some((filter) => {
|
||||
const filterPattern = new RegExp('^' + filter.replace(/\*/g, '.*') + '$');
|
||||
return filterPattern.test(filter);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// If no matching rules found, return !strictMode
|
||||
return !strictMode;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user