diff --git a/lerna.json b/lerna.json index 21cad51eca..7c407fbe08 100644 --- a/lerna.json +++ b/lerna.json @@ -2,9 +2,7 @@ "version": "1.6.0-alpha.25", "npmClient": "yarn", "useWorkspaces": true, - "npmClientArgs": [ - "--ignore-engines" - ], + "npmClientArgs": ["--ignore-engines"], "command": { "version": { "forcePublish": true, diff --git a/packages/core/client/src/application/Application.tsx b/packages/core/client/src/application/Application.tsx index 41b18e3f85..0131c53583 100644 --- a/packages/core/client/src/application/Application.tsx +++ b/packages/core/client/src/application/Application.tsx @@ -44,8 +44,14 @@ import type { CollectionFieldInterfaceFactory } from '../data-source'; import { OpenModeProvider } from '../modules/popup/OpenModeProvider'; import { AppSchemaComponentProvider } from './AppSchemaComponentProvider'; import type { Plugin } from './Plugin'; +import { getOperators } from './globalOperators'; import type { RequireJS } from './utils/requirejs'; +type JsonLogic = { + addOperation: (name: string, fn?: any) => void; + rmOperation: (name: string) => void; +}; + declare global { interface Window { define: RequireJS['define']; @@ -100,7 +106,7 @@ export class Application { public dataSourceManager: DataSourceManager; public name: string; public globalVars: Record = {}; - + public jsonLogic: JsonLogic; loading = true; maintained = false; maintaining = false; @@ -155,6 +161,7 @@ export class Application { this.apiClient.auth.locale = lng; }); this.initListeners(); + this.jsonLogic = getOperators(); } private initListeners() { @@ -488,4 +495,11 @@ export class Application { getGlobalVar(key) { return get(this.globalVars, key); } + + registerOperators(key, operator) { + this.jsonLogic[key] = operator; + } + getOperator(key) { + return this.jsonLogic[key]; + } } diff --git a/packages/core/client/src/schema-component/common/utils/logic.js b/packages/core/client/src/application/globalOperators.js similarity index 90% rename from packages/core/client/src/schema-component/common/utils/logic.js rename to packages/core/client/src/application/globalOperators.js index 9acad63bbd..c3b16067e8 100644 --- a/packages/core/client/src/schema-component/common/utils/logic.js +++ b/packages/core/client/src/application/globalOperators.js @@ -9,13 +9,11 @@ /* globals define,module */ -import dayjs from 'dayjs'; - /* Using a Universal Module Loader that should be browser, require, and AMD friendly http://ricostacruz.com/cheatsheets/umdjs.html */ -export function getJsonLogic() { +export function getOperators() { 'use strict'; /* globals console:false */ @@ -307,11 +305,11 @@ export function getJsonLogic() { }, missing: function () { /* - Missing can receive many keys as many arguments, like {"missing:[1,2]} - Missing can also receive *one* argument that is an array of keys, - which typically happens if it's actually acting on the output of another command - (like 'if' or 'merge') - */ + Missing can receive many keys as many arguments, like {"missing:[1,2]} + Missing can also receive *one* argument that is an array of keys, + which typically happens if it's actually acting on the output of another command + (like 'if' or 'merge') + */ var missing = []; var keys = Array.isArray(arguments[0]) ? arguments[0] : arguments; @@ -348,10 +346,10 @@ export function getJsonLogic() { }; /* - This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. - - Spec and rationale here: http://jsonlogic.com/truthy - */ + This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer. + + Spec and rationale here: http://jsonlogic.com/truthy + */ jsonLogic.truthy = function (value) { if (Array.isArray(value) && value.length === 0) { return false; @@ -359,12 +357,12 @@ export function getJsonLogic() { return !!value; }; - jsonLogic.get_operator = function (logic) { + jsonLogic.getOperator = function (logic) { return Object.keys(logic)[0]; }; - jsonLogic.get_values = function (logic) { - return logic[jsonLogic.get_operator(logic)]; + jsonLogic.getValues = function (logic) { + return logic[jsonLogic.getOperator(logic)]; }; jsonLogic.apply = function (logic, data) { @@ -379,7 +377,7 @@ export function getJsonLogic() { return logic; } - var op = jsonLogic.get_operator(logic); + var op = jsonLogic.getOperator(logic); var values = logic[op]; var i; var current; @@ -395,18 +393,18 @@ export function getJsonLogic() { // 'if', 'and', and 'or' violate the normal rule of depth-first calculating consequents, let each manage recursion as needed. if (op === 'if' || op == '?:') { /* 'if' should be called with a odd number of parameters, 3 or greater - This works on the pattern: - if( 0 ){ 1 }else{ 2 }; - if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; - if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; - - The implementation is: - For pairs of values (0,1 then 2,3 then 4,5 etc) - If the first evaluates truthy, evaluate and return the second - If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) - given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) - given 0 parameters, return NULL (not great practice, but there was no Else) - */ + This works on the pattern: + if( 0 ){ 1 }else{ 2 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 }; + if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 }; + + The implementation is: + For pairs of values (0,1 then 2,3 then 4,5 etc) + If the first evaluates truthy, evaluate and return the second + If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3) + given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false) + given 0 parameters, return NULL (not great practice, but there was no Else) + */ for (i = 0; i < values.length - 1; i += 2) { if (jsonLogic.truthy(jsonLogic.apply(values[i], data))) { return jsonLogic.apply(values[i + 1], data); @@ -543,7 +541,7 @@ export function getJsonLogic() { var collection = []; if (jsonLogic.is_logic(logic)) { - var op = jsonLogic.get_operator(logic); + var op = jsonLogic.getOperator(logic); var values = logic[op]; if (!Array.isArray(values)) { @@ -564,11 +562,11 @@ export function getJsonLogic() { return arrayUnique(collection); }; - jsonLogic.add_operation = function (name, code) { + jsonLogic.addOperation = function (name, code) { operations[name] = code; }; - jsonLogic.rm_operation = function (name) { + jsonLogic.rmOperation = function (name) { delete operations[name]; }; @@ -593,8 +591,8 @@ export function getJsonLogic() { if (jsonLogic.is_logic(pattern)) { if (jsonLogic.is_logic(rule)) { - var pattern_op = jsonLogic.get_operator(pattern); - var rule_op = jsonLogic.get_operator(rule); + var pattern_op = jsonLogic.getOperator(pattern); + var rule_op = jsonLogic.getOperator(rule); if (pattern_op === '@' || pattern_op === rule_op) { // echo "\nOperators match, go deeper\n"; @@ -610,8 +608,8 @@ export function getJsonLogic() { return false; } /* - Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT) - */ + Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT) + */ for (var i = 0; i < pattern.length; i += 1) { // If any fail, we fail if (!jsonLogic.rule_like(rule[i], pattern[i])) { diff --git a/packages/core/client/src/schema-component/antd/action/Action.tsx b/packages/core/client/src/schema-component/antd/action/Action.tsx index 4d37f22a7d..4f89630525 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.tsx @@ -47,6 +47,7 @@ import { ActionContextProvider } from './context'; import { useGetAriaLabelOfAction } from './hooks/useGetAriaLabelOfAction'; import { ActionContextProps, ActionProps, ComposedAction } from './types'; import { linkageAction, setInitialActionState } from './utils'; +import { useApp } from '../../../application'; const useA = () => { return { @@ -95,7 +96,7 @@ export const Action: ComposedAction = withDynamicSchemaProps( const { setSubmitted } = useActionContext(); const { getAriaLabel } = useGetAriaLabelOfAction(title); const parentRecordData = useCollectionParentRecordData(); - + const app = useApp(); useEffect(() => { if (field.stateOfLinkageRules) { setInitialActionState(field); @@ -105,13 +106,16 @@ export const Action: ComposedAction = withDynamicSchemaProps( .filter((k) => !k.disabled) .forEach((v) => { v.actions?.forEach((h) => { - linkageAction({ - operator: h.operator, - field, - condition: v.condition, - variables, - localVariables, - }); + linkageAction( + { + operator: h.operator, + field, + condition: v.condition, + variables, + localVariables, + }, + app.jsonLogic, + ); }); }); }, [field, linkageRules, localVariables, variables]); diff --git a/packages/core/client/src/schema-component/antd/action/utils.ts b/packages/core/client/src/schema-component/antd/action/utils.ts index 5e7677ffec..5254021916 100644 --- a/packages/core/client/src/schema-component/antd/action/utils.ts +++ b/packages/core/client/src/schema-component/antd/action/utils.ts @@ -80,25 +80,28 @@ export const requestSettingsSchema: ISchema = { }, }; -export const linkageAction = async ({ - operator, - field, - condition, - variables, - localVariables, -}: { - operator; - field; - condition; - variables: VariablesContextType; - localVariables: VariableOption[]; -}) => { +export const linkageAction = async ( + { + operator, + field, + condition, + variables, + localVariables, + }: { + operator; + field; + condition; + variables: VariablesContextType; + localVariables: VariableOption[]; + }, + jsonLogic: any, +) => { const disableResult = field?.stateOfLinkageRules?.disabled || [false]; const displayResult = field?.stateOfLinkageRules?.display || ['visible']; switch (operator) { case ActionType.Visible: - if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables })) { + if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic)) { displayResult.push(operator); field.data = field.data || {}; field.data.hidden = false; @@ -110,7 +113,7 @@ export const linkageAction = async ({ field.display = last(displayResult); break; case ActionType.Hidden: - if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables })) { + if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic)) { field.data = field.data || {}; field.data.hidden = true; } else { @@ -119,7 +122,7 @@ export const linkageAction = async ({ } break; case ActionType.Disabled: - if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables })) { + if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic)) { disableResult.push(true); } field.stateOfLinkageRules = { @@ -130,7 +133,7 @@ export const linkageAction = async ({ field.componentProps['disabled'] = last(disableResult); break; case ActionType.Active: - if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables })) { + if (await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic)) { disableResult.push(false); } else { disableResult.push(!!field.componentProps?.['disabled']); diff --git a/packages/core/client/src/schema-component/antd/form-item/hooks/useLinkageRulesForSubTableOrSubForm.ts b/packages/core/client/src/schema-component/antd/form-item/hooks/useLinkageRulesForSubTableOrSubForm.ts index f59ba764b7..2fc7a4b2a8 100644 --- a/packages/core/client/src/schema-component/antd/form-item/hooks/useLinkageRulesForSubTableOrSubForm.ts +++ b/packages/core/client/src/schema-component/antd/form-item/hooks/useLinkageRulesForSubTableOrSubForm.ts @@ -16,6 +16,7 @@ import { forEachLinkageRule } from '../../../../schema-settings/LinkageRules/for import useLocalVariables from '../../../../variables/hooks/useLocalVariables'; import useVariables from '../../../../variables/hooks/useVariables'; import { useSubFormValue } from '../../association-field/hooks'; +import { useApp } from '../../../../application'; import { isSubMode } from '../../association-field/util'; const isSubFormOrSubTableField = (fieldSchema: Schema) => { @@ -45,6 +46,7 @@ export const useLinkageRulesForSubTableOrSubForm = () => { const variables = useVariables(); const linkageRules = getLinkageRules(schemaOfSubTableOrSubForm); + const app = useApp(); useEffect(() => { if (!isSubFormOrSubTableField(fieldSchema)) { @@ -77,16 +79,19 @@ export const useLinkageRulesForSubTableOrSubForm = () => { forEachLinkageRule(linkageRules, (action, rule) => { if (action.targetFields?.includes(fieldSchema.name)) { disposes.push( - bindLinkageRulesToFiled({ - field, - linkageRules, - formValues: formValue, - localVariables, - action, - rule, - variables, - variableNameOfLeftCondition: '$iteration', - }), + bindLinkageRulesToFiled( + { + field, + linkageRules, + formValues: formValue, + localVariables, + action, + rule, + variables, + variableNameOfLeftCondition: '$iteration', + }, + app.jsonLogic, + ), ); } }); diff --git a/packages/core/client/src/schema-component/antd/form-v2/Form.tsx b/packages/core/client/src/schema-component/antd/form-v2/Form.tsx index d304ba463b..f099766a90 100644 --- a/packages/core/client/src/schema-component/antd/form-v2/Form.tsx +++ b/packages/core/client/src/schema-component/antd/form-v2/Form.tsx @@ -27,6 +27,7 @@ import { useToken } from '../../../style'; import { useLocalVariables, useVariables } from '../../../variables'; import { useProps } from '../../hooks/useProps'; import { useFormBlockHeight } from './hook'; +import { useApp } from '../../../application'; export interface FormProps extends IFormLayoutProps { form?: FormilyForm; @@ -136,6 +137,7 @@ const WithForm = (props: WithFormProps) => { const localVariables = useLocalVariables({ currentForm: form }); const { templateFinished } = useTemplateBlockContext(); const { loading } = useDataBlockRequest() || {}; + const app = useApp(); const linkageRules: any[] = (getLinkageRules(fieldSchema) || fieldSchema.parent?.['x-linkage-rules'])?.filter((k) => !k.disabled) || []; @@ -175,15 +177,18 @@ const WithForm = (props: WithFormProps) => { // 之前使用的 `onFieldReact` 有问题,没有办法被取消监听,所以这里用 `onFieldInit` 和 `reaction` 代替 onFieldInit(`*(${fields})`, (field: any, form) => { disposes.push( - bindLinkageRulesToFiled({ - field, - linkageRules, - formValues: form.values, - localVariables, - action, - rule, - variables, - }), + bindLinkageRulesToFiled( + { + field, + linkageRules, + formValues: form.values, + localVariables, + action, + rule, + variables, + }, + app.jsonLogic, + ), ); }); } diff --git a/packages/core/client/src/schema-component/common/utils/uitls.tsx b/packages/core/client/src/schema-component/common/utils/uitls.tsx index 836abdbcc2..198f98cfa5 100644 --- a/packages/core/client/src/schema-component/common/utils/uitls.tsx +++ b/packages/core/client/src/schema-component/common/utils/uitls.tsx @@ -14,7 +14,7 @@ import { VariableOption, VariablesContextType } from '../../../variables/types'; import { isVariable } from '../../../variables/utils/isVariable'; import { transformVariableValue } from '../../../variables/utils/transformVariableValue'; import { inferPickerType } from '../../antd/date-picker/util'; -import { getJsonLogic } from '../../common/utils/logic'; + type VariablesCtx = { /** 当前登录的用户 */ $user?: Record; @@ -76,31 +76,34 @@ function getAllKeys(obj) { return keys; } -export const conditionAnalyses = async ({ - ruleGroup, - variables, - localVariables, - variableNameOfLeftCondition, -}: { - ruleGroup; - variables: VariablesContextType; - localVariables: VariableOption[]; - /** - * used to parse the variable name of the left condition value - * @default '$nForm' - */ - variableNameOfLeftCondition?: string; -}) => { +export const conditionAnalyses = async ( + { + ruleGroup, + variables, + localVariables, + variableNameOfLeftCondition, + }: { + ruleGroup; + variables: VariablesContextType; + localVariables: VariableOption[]; + /** + * used to parse the variable name of the left condition value + * @default '$nForm' + */ + variableNameOfLeftCondition?: string; + }, + jsonLogic: any, +) => { const type = Object.keys(ruleGroup)[0] || '$and'; const conditions = ruleGroup[type]; let results = conditions.map(async (condition) => { if ('$and' in condition || '$or' in condition) { - return await conditionAnalyses({ ruleGroup: condition, variables, localVariables }); + return await conditionAnalyses({ ruleGroup: condition, variables, localVariables }, jsonLogic); } - const jsonlogic = getInnermostKeyAndValue(condition); - const operator = jsonlogic?.key; + const logicCalculation = getInnermostKeyAndValue(condition); + const operator = logicCalculation?.key; if (!operator) { return true; @@ -113,12 +116,11 @@ export const conditionAnalyses = async ({ }) .then(({ value }) => value); - const parsingResult = isVariable(jsonlogic?.value) - ? [variables.parseVariable(jsonlogic?.value, localVariables).then(({ value }) => value), targetValue] - : [jsonlogic?.value, targetValue]; + const parsingResult = isVariable(logicCalculation?.value) + ? [variables.parseVariable(logicCalculation?.value, localVariables).then(({ value }) => value), targetValue] + : [logicCalculation?.value, targetValue]; try { - const jsonLogic = getJsonLogic(); const [value, targetValue] = await Promise.all(parsingResult); const targetCollectionField = await variables.getCollectionField(targetVariableName, localVariables); let currentInputValue = transformVariableValue(targetValue, { targetCollectionField }); diff --git a/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx b/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx index 0fc08b9b60..a940d82085 100644 --- a/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx +++ b/packages/core/client/src/schema-initializer/components/CreateRecordAction.tsx @@ -22,6 +22,7 @@ import { linkageAction } from '../../schema-component/antd/action/utils'; import { usePopupUtils } from '../../schema-component/antd/page/pagePopupUtils'; import { parseVariables } from '../../schema-component/common/utils/uitls'; import { useLocalVariables, useVariables } from '../../variables'; +import { useApp } from '../../application'; export function useAclCheck(actionPath) { const aclCheck = useAclCheckFn(); @@ -73,6 +74,7 @@ const InternalCreateRecordAction = (props: any, ref) => { const { openPopup } = usePopupUtils(); const treeRecordData = useTreeParentRecord(); const cm = useCollectionManager(); + const app = useApp(); useEffect(() => { field.stateOfLinkageRules = {}; @@ -80,13 +82,16 @@ const InternalCreateRecordAction = (props: any, ref) => { .filter((k) => !k.disabled) .forEach((v) => { v.actions?.forEach((h) => { - linkageAction({ - operator: h.operator, - field, - condition: v.condition, - variables, - localVariables, - }); + linkageAction( + { + operator: h.operator, + field, + condition: v.condition, + variables, + localVariables, + }, + app.jsonLogic, + ); }); }); }, [field, linkageRules, localVariables, variables]); @@ -143,7 +148,6 @@ export const CreateAction = observer( const form = useForm(); const variables = useVariables(); const aclCheck = useAclCheckFn(); - const enableChildren = fieldSchema['x-enable-children'] || []; const allowAddToCurrent = fieldSchema?.['x-allow-add-to-current']; const linkageFromForm = fieldSchema?.['x-component-props']?.['linkageFromForm']; @@ -176,6 +180,7 @@ export const CreateAction = observer( const compile = useCompile(); const { designable } = useDesignable(); const icon = props.icon || null; + const app = useApp(); const menuItems = useMemo(() => { return inheritsCollections.map((option) => ({ key: option.name, @@ -196,13 +201,16 @@ export const CreateAction = observer( .filter((k) => !k.disabled) .forEach((v) => { v.actions?.forEach((h) => { - linkageAction({ - operator: h.operator, - field, - condition: v.condition, - variables, - localVariables, - }); + linkageAction( + { + operator: h.operator, + field, + condition: v.condition, + variables, + localVariables, + }, + app.jsonLogic, + ); }); }); }, [field, linkageRules, localVariables, variables]); diff --git a/packages/core/client/src/schema-settings/LinkageRules/bindLinkageRulesToFiled.ts b/packages/core/client/src/schema-settings/LinkageRules/bindLinkageRulesToFiled.ts index 091287b69b..be17782a4e 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/bindLinkageRulesToFiled.ts +++ b/packages/core/client/src/schema-settings/LinkageRules/bindLinkageRulesToFiled.ts @@ -39,29 +39,32 @@ interface Props { variableNameOfLeftCondition?: string; } -export function bindLinkageRulesToFiled({ - field, - linkageRules, - formValues, - localVariables, - action, - rule, - variables, - variableNameOfLeftCondition, -}: { - field: any; - linkageRules: any[]; - formValues: any; - localVariables: VariableOption[]; - action: any; - rule: any; - variables: VariablesContextType; - /** - * used to parse the variable name of the left condition value - * @default '$nForm' - */ - variableNameOfLeftCondition?: string; -}) { +export function bindLinkageRulesToFiled( + { + field, + linkageRules, + formValues, + localVariables, + action, + rule, + variables, + variableNameOfLeftCondition, + }: { + field: any; + linkageRules: any[]; + formValues: any; + localVariables: VariableOption[]; + action: any; + rule: any; + variables: VariablesContextType; + /** + * used to parse the variable name of the left condition value + * @default '$nForm' + */ + variableNameOfLeftCondition?: string; + }, + jsonLogic: any, +) { field['initStateOfLinkageRules'] = { display: field.initStateOfLinkageRules?.display || getTempFieldState(true, field.display), required: field.initStateOfLinkageRules?.required || getTempFieldState(true, field.required || false), @@ -89,7 +92,7 @@ export function bindLinkageRulesToFiled({ .join(','); return result; }, - getSubscriber({ action, field, rule, variables, localVariables, variableNameOfLeftCondition }), + getSubscriber({ action, field, rule, variables, localVariables, variableNameOfLeftCondition }, jsonLogic), { fireImmediately: true, equals: _.isEqual }, ); } @@ -176,36 +179,42 @@ function getVariableValue(variableString: string, localVariables: VariableOption return getValuesByPath(ctx, getPath(variableString)); } -function getSubscriber({ - action, - field, - rule, - variables, - localVariables, - variableNameOfLeftCondition, -}: { - action: any; - field: any; - rule: any; - variables: VariablesContextType; - localVariables: VariableOption[]; - /** - * used to parse the variable name of the left condition value - * @default '$nForm' - */ - variableNameOfLeftCondition?: string; -}): (value: string, oldValue: string) => void { +function getSubscriber( + { + action, + field, + rule, + variables, + localVariables, + variableNameOfLeftCondition, + }: { + action: any; + field: any; + rule: any; + variables: VariablesContextType; + localVariables: VariableOption[]; + /** + * used to parse the variable name of the left condition value + * @default '$nForm' + */ + variableNameOfLeftCondition?: string; + }, + jsonLogic, +): (value: string, oldValue: string) => void { return () => { // 当条件改变触发 reaction 时,会同步收集字段状态,并保存到 field.stateOfLinkageRules 中 - collectFieldStateOfLinkageRules({ - operator: action.operator, - value: action.value, - field, - condition: rule.condition, - variables, - localVariables, - variableNameOfLeftCondition, - }); + collectFieldStateOfLinkageRules( + { + operator: action.operator, + value: action.value, + field, + condition: rule.condition, + variables, + localVariables, + variableNameOfLeftCondition, + }, + jsonLogic, + ); // 当条件改变时,有可能会触发多个 reaction,所以这里需要延迟一下,确保所有的 reaction 都执行完毕后, // 再从 field.stateOfLinkageRules 中取值,因为此时 field.stateOfLinkageRules 中的值才是全的。 @@ -286,15 +295,10 @@ function getFieldNameByOperator(operator: ActionType) { } } -export const collectFieldStateOfLinkageRules = ({ - operator, - value, - field, - condition, - variables, - localVariables, - variableNameOfLeftCondition, -}: Props) => { +export const collectFieldStateOfLinkageRules = ( + { operator, value, field, condition, variables, localVariables, variableNameOfLeftCondition }: Props, + jsonLogic: any, +) => { const requiredResult = field?.stateOfLinkageRules?.required || [field?.initStateOfLinkageRules?.required]; const displayResult = field?.stateOfLinkageRules?.display || [field?.initStateOfLinkageRules?.display]; const patternResult = field?.stateOfLinkageRules?.pattern || [field?.initStateOfLinkageRules?.pattern]; @@ -304,14 +308,14 @@ export const collectFieldStateOfLinkageRules = ({ switch (operator) { case ActionType.Required: - requiredResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult), true)); + requiredResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult, jsonLogic), true)); field.stateOfLinkageRules = { ...field.stateOfLinkageRules, required: requiredResult, }; break; case ActionType.InRequired: - requiredResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult), false)); + requiredResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult, jsonLogic), false)); field.stateOfLinkageRules = { ...field.stateOfLinkageRules, required: requiredResult, @@ -320,7 +324,7 @@ export const collectFieldStateOfLinkageRules = ({ case ActionType.Visible: case ActionType.None: case ActionType.Hidden: - displayResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult), operator)); + displayResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult, jsonLogic), operator)); field.stateOfLinkageRules = { ...field.stateOfLinkageRules, display: displayResult, @@ -329,7 +333,7 @@ export const collectFieldStateOfLinkageRules = ({ case ActionType.Editable: case ActionType.ReadOnly: case ActionType.ReadPretty: - patternResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult), operator)); + patternResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult, jsonLogic), operator)); field.stateOfLinkageRules = { ...field.stateOfLinkageRules, pattern: patternResult, @@ -364,7 +368,7 @@ export const collectFieldStateOfLinkageRules = ({ if (isConditionEmpty(condition)) { valueResult.push(getTempFieldState(true, getValue())); } else { - valueResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult), getValue())); + valueResult.push(getTempFieldState(conditionAnalyses(paramsToGetConditionResult, jsonLogic), getValue())); } field.stateOfLinkageRules = { ...field.stateOfLinkageRules, diff --git a/packages/core/client/src/schema-settings/LinkageRules/compute-rules.ts b/packages/core/client/src/schema-settings/LinkageRules/compute-rules.ts index 2e56cc8a1c..2c8daf81ca 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/compute-rules.ts +++ b/packages/core/client/src/schema-settings/LinkageRules/compute-rules.ts @@ -25,13 +25,13 @@ const getActionValue = (operator, value) => { } }; -const getSatisfiedActions = async ({ rules, variables, localVariables }) => { +const getSatisfiedActions = async ({ rules, variables, localVariables }, jsonLogic) => { const satisfiedRules = ( await Promise.all( rules .filter((k) => !k.disabled) .map(async (rule) => { - if (await conditionAnalyses({ ruleGroup: rule.condition, variables, localVariables })) { + if (await conditionAnalyses({ ruleGroup: rule.condition, variables, localVariables }, jsonLogic)) { return rule; } else return null; }), @@ -40,15 +40,15 @@ const getSatisfiedActions = async ({ rules, variables, localVariables }) => { return satisfiedRules.map((rule) => rule.actions).flat(); }; -const getSatisfiedValues = async ({ rules, variables, localVariables }) => { - return (await getSatisfiedActions({ rules, variables, localVariables })).map((action) => ({ +const getSatisfiedValues = async ({ rules, variables, localVariables }, jsonLogic) => { + return (await getSatisfiedActions({ rules, variables, localVariables }, jsonLogic)).map((action) => ({ ...action, value: getActionValue(action.operator, action.value), })); }; -export const getSatisfiedValueMap = async ({ rules, variables, localVariables }) => { - const values = await getSatisfiedValues({ rules, variables, localVariables }); +export const getSatisfiedValueMap = async ({ rules, variables, localVariables }, jsonLogic) => { + const values = await getSatisfiedValues({ rules, variables, localVariables }, jsonLogic); const valueMap = values.reduce((a, v) => ({ ...a, [v.operator]: v.value }), {}); return valueMap; }; diff --git a/packages/core/client/src/schema-settings/LinkageRules/useActionValues.ts b/packages/core/client/src/schema-settings/LinkageRules/useActionValues.ts index f54ee21e0d..eb857fc6cf 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/useActionValues.ts +++ b/packages/core/client/src/schema-settings/LinkageRules/useActionValues.ts @@ -15,7 +15,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useLocalVariables, useVariables } from '../../variables'; import { getSatisfiedValueMap } from './compute-rules'; import { LinkageRuleCategory, LinkageRuleDataKeyMap } from './type'; - +import { useApp } from '../../application'; export function useSatisfiedActionValues({ formValues, category = 'default', @@ -35,10 +35,11 @@ export function useSatisfiedActionValues({ const localVariables = useLocalVariables({ currentForm: { values: formValues } as any }); const localSchema = schema ?? fieldSchema; const styleRules = rules ?? localSchema[LinkageRuleDataKeyMap[category]]; + const app = useApp(); const compute = useCallback(() => { if (styleRules && formValues) { - getSatisfiedValueMap({ rules: styleRules, variables, localVariables }) + getSatisfiedValueMap({ rules: styleRules, variables, localVariables }, app.jsonLogic) .then((valueMap) => { if (!isEmpty(valueMap)) { setValueMap(valueMap); diff --git a/packages/plugins/@nocobase/plugin-field-sequence/src/client/sequence.tsx b/packages/plugins/@nocobase/plugin-field-sequence/src/client/sequence.tsx index 501016b9a1..0d6ccc1826 100644 --- a/packages/plugins/@nocobase/plugin-field-sequence/src/client/sequence.tsx +++ b/packages/plugins/@nocobase/plugin-field-sequence/src/client/sequence.tsx @@ -121,9 +121,11 @@ const RuleTypes = { number: t('Number', { ns: NAMESPACE }), lowercase: t('Lowercase letters', { ns: NAMESPACE }), uppercase: t('Uppercase letters', { ns: NAMESPACE }), - symbol: t('Symbols', { ns: NAMESPACE }) + symbol: t('Symbols', { ns: NAMESPACE }), }; - return {value?.map(charset => charsetLabels[charset]).join(', ') || t('Number', { ns: NAMESPACE })}; + return ( + {value?.map((charset) => charsetLabels[charset]).join(', ') || t('Number', { ns: NAMESPACE })} + ); }, }, fieldset: { @@ -154,14 +156,14 @@ const RuleTypes = { { value: 'number', label: `{{t("Number", { ns: "${NAMESPACE}" })}}` }, { value: 'lowercase', label: `{{t("Lowercase letters", { ns: "${NAMESPACE}" })}}` }, { value: 'uppercase', label: `{{t("Uppercase letters", { ns: "${NAMESPACE}" })}}` }, - { value: 'symbol', label: `{{t("Symbols", { ns: "${NAMESPACE}" })}}` } + { value: 'symbol', label: `{{t("Symbols", { ns: "${NAMESPACE}" })}}` }, ], required: true, default: ['number'], 'x-validator': { minItems: 1, - message: `{{t("At least one character set should be selected", { ns: "${NAMESPACE}" })}}` - } + message: `{{t("At least one character set should be selected", { ns: "${NAMESPACE}" })}}`, + }, }, }, defaults: { diff --git a/packages/plugins/@nocobase/plugin-field-sequence/src/server/fields/sequence-field.ts b/packages/plugins/@nocobase/plugin-field-sequence/src/server/fields/sequence-field.ts index 388feef336..1478a11615 100644 --- a/packages/plugins/@nocobase/plugin-field-sequence/src/server/fields/sequence-field.ts +++ b/packages/plugins/@nocobase/plugin-field-sequence/src/server/fields/sequence-field.ts @@ -301,7 +301,7 @@ const CHAR_SETS = { lowercase: 'abcdefghijklmnopqrstuvwxyz', uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', // 符号只保留常用且安全的符号,有需要的可以自己加比如[]{}|;:,.<>放在链接或者文件名里容易出问题的字符 - symbol: '!@#$%^&*_-+' + symbol: '!@#$%^&*_-+', } as const; interface RandomCharOptions { @@ -317,21 +317,16 @@ sequencePatterns.register('randomChar', { if (!options?.charsets || options.charsets.length === 0) { return 'At least one character set should be selected'; } - if (options.charsets.some(charset => !CHAR_SETS[charset])) { + if (options.charsets.some((charset) => !CHAR_SETS[charset])) { return 'Invalid charset selected'; } return null; }, - - generate(instance: any, options: RandomCharOptions) { - const { - length = 6, - charsets = ['number'] - } = options; - const chars = [...new Set( - charsets.reduce((acc, charset) => acc + CHAR_SETS[charset], '') - )]; + generate(instance: any, options: RandomCharOptions) { + const { length = 6, charsets = ['number'] } = options; + + const chars = [...new Set(charsets.reduce((acc, charset) => acc + CHAR_SETS[charset], ''))]; const getRandomChar = () => { const randomIndex = Math.floor(Math.random() * chars.length); @@ -352,20 +347,27 @@ sequencePatterns.register('randomChar', { }, getMatcher(options: RandomCharOptions) { - const pattern = [...new Set( - (options.charsets || ['number']).reduce((acc, charset) => { - switch (charset) { - case 'number': return acc + '0-9'; - case 'lowercase': return acc + 'a-z'; - case 'uppercase': return acc + 'A-Z'; - case 'symbol': return acc + CHAR_SETS.symbol.replace('-', '').replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + '-'; - default: return acc; - } - }, '') - )].join(''); + const pattern = [ + ...new Set( + (options.charsets || ['number']).reduce((acc, charset) => { + switch (charset) { + case 'number': + return acc + '0-9'; + case 'lowercase': + return acc + 'a-z'; + case 'uppercase': + return acc + 'A-Z'; + case 'symbol': + return acc + CHAR_SETS.symbol.replace('-', '').replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + '-'; + default: + return acc; + } + }, ''), + ), + ].join(''); return `[${pattern}]{${options.length || 6}}`; - } + }, }); interface PatternConfig {