From 457082ffa11f44b3b3f5f40715800174021cf96d Mon Sep 17 00:00:00 2001 From: katherinehhh Date: Fri, 4 Aug 2023 18:07:58 +0800 Subject: [PATCH] refractor(remote-select): association field data scope merge with original filter conditions (#2118) * fix: deleting the last field from sub table, the entire table will be deleted * refactor: association select support data scope in sub-table * refactor: locale improve * style: sub-table style improve * refactor: code improve * chore: association field data scope * refactor: code improve * refactor: code improve --- .../antd/association-field/hooks.ts | 75 ++++++++++++++++++- .../antd/association-field/util.ts | 35 +++++++++ .../antd/remote-select/RemoteSelect.tsx | 72 +----------------- .../antd/remote-select/utils.ts | 35 --------- 4 files changed, 110 insertions(+), 107 deletions(-) delete mode 100644 packages/core/client/src/schema-component/antd/remote-select/utils.ts diff --git a/packages/core/client/src/schema-component/antd/association-field/hooks.ts b/packages/core/client/src/schema-component/antd/association-field/hooks.ts index 203249a95..78a8fb889 100644 --- a/packages/core/client/src/schema-component/antd/association-field/hooks.ts +++ b/packages/core/client/src/schema-component/antd/association-field/hooks.ts @@ -1,13 +1,18 @@ import { GeneralField } from '@formily/core'; -import { useFieldSchema } from '@formily/react'; +import { useField, useFieldSchema, useForm } from '@formily/react'; +import flat from 'flat'; +import { isString } from 'lodash'; import cloneDeep from 'lodash/cloneDeep'; import { useCallback, useContext, useMemo } from 'react'; +import { useBlockRequestContext } from '../../../block-provider/BlockProvider'; import { mergeFilter } from '../../../block-provider/SharedFilterProvider'; import { useCollection, useCollectionManager } from '../../../collection-manager'; import { isInFilterFormBlock } from '../../../filter-provider'; import { useRecord } from '../../../record-provider'; +import { getInnermostKeyAndValue } from '../../common/utils/uitls'; import { useDesignable } from '../../hooks'; import { AssociationFieldContext } from './context'; +import { extractFilterfield, extractValuesByPattern, generatePattern, parseVariables } from './util'; export const useInsertSchema = (component) => { const fieldSchema = useFieldSchema(); @@ -43,9 +48,75 @@ export default function useServiceOptions(props) { const { action = 'list', service, fieldNames } = props; const params = service?.params || {}; const fieldSchema = useFieldSchema(); + const field = useField(); + const form = useForm(); + const ctx = useBlockRequestContext(); const { getField } = useCollection(); const { getCollectionFields, getCollectionJoinField } = useCollectionManager(); const record = useRecord(); + const parseFilter = useCallback( + (rules) => { + if (!rules) { + return undefined; + } + if (typeof rules === 'string') { + return rules; + } + const type = Object.keys(rules)[0] || '$and'; + const conditions = rules[type]; + const results = []; + conditions?.forEach((c) => { + const jsonlogic = getInnermostKeyAndValue(c); + const regex = /{{(.*?)}}/; + const matches = jsonlogic.value?.match?.(regex); + if (!matches || (!matches[1].includes('$form') && !matches[1].includes('$iteration'))) { + results.push(c); + return; + } + const associationfield = extractFilterfield(matches[1]); + const filterCollectionField = getCollectionJoinField(`${ctx.props.collection}.${associationfield}`); + if (['o2m', 'm2m'].includes(filterCollectionField?.interface)) { + // 对多子表单 + const pattern = generatePattern(matches?.[1], associationfield); + const parseValue: any = extractValuesByPattern(flat(form.values), pattern); + const filters = parseValue.map((v) => { + return JSON.parse(JSON.stringify(c).replace(jsonlogic.value, v)); + }); + results.push({ $or: filters }); + } else { + const variablesCtx = { $form: form.values, $iteration: form.values }; + let str = matches?.[1]; + if (str.includes('$iteration')) { + const path = field.path.segments.concat([]); + path.pop(); + str = str.replace('$iteration.', `$iteration.${path.join('.')}.`); + } + const parseValue = parseVariables(str, variablesCtx); + if (Array.isArray(parseValue)) { + const filters = parseValue.map((v) => { + return JSON.parse(JSON.stringify(c).replace(jsonlogic.value, v)); + }); + results.push({ $or: filters }); + } else { + const filterObj = JSON.parse( + JSON.stringify(c).replace(jsonlogic.value, str.endsWith('id') ? parseValue ?? 0 : parseValue), + ); + results.push(filterObj); + } + } + }); + return { [type]: results }; + }, + [ctx.props?.collection, field.path.segments, form.values, getCollectionJoinField], + ); + + const fieldServiceFilter = useMemo(() => { + const filterFromSchema = isString(fieldSchema?.['x-component-props']?.service?.params?.filter) + ? field.componentProps?.service?.params?.filter + : fieldSchema?.['x-component-props']?.service?.params?.filter; + + return mergeFilter([parseFilter(filterFromSchema) || service?.params?.filter]); + }, [field.componentProps?.service?.params?.filter, fieldSchema, parseFilter, service?.params?.filter]); const normalizeValues = useCallback( (obj) => { @@ -85,7 +156,7 @@ export default function useServiceOptions(props) { }, } : null, - params?.filter, + fieldServiceFilter, ]), isOToAny && sourceValue !== undefined && diff --git a/packages/core/client/src/schema-component/antd/association-field/util.ts b/packages/core/client/src/schema-component/antd/association-field/util.ts index da46758e7..30f603b7a 100644 --- a/packages/core/client/src/schema-component/antd/association-field/util.ts +++ b/packages/core/client/src/schema-component/antd/association-field/util.ts @@ -2,6 +2,7 @@ import { ISchema } from '@formily/react'; import { isArr } from '@formily/shared'; import { getDefaultFormat, str2moment } from '@nocobase/utils/client'; import { Tag } from 'antd'; +import { get, isFunction } from 'lodash'; import dayjs from 'dayjs'; import React from 'react'; import { CollectionFieldOptions, useCollectionManager } from '../../../collection-manager'; @@ -93,3 +94,37 @@ export const toValue = (value, placeholder) => { } return value; }; + +export const parseVariables = (str: string, ctx) => { + if (str) { + const result = get(ctx, str); + return isFunction(result) ? result() : result; + } else { + return str; + } +}; +export function extractFilterfield(str) { + const match = str.match(/^\$form\.([^.[\]]+)/); + if (match) { + return match[1]; + } + return null; +} + +export function extractValuesByPattern(obj, pattern) { + const regexPattern = new RegExp(pattern.replace(/\*/g, '\\d+')); + const result = []; + + for (const key in obj) { + if (regexPattern.test(key)) { + const value = obj[key]; + result.push(value); + } + } + + return result; +} +export function generatePattern(str, fieldName) { + const result = str.replace(`$form.${fieldName}.`, `${fieldName}.*.`); + return result; +} diff --git a/packages/core/client/src/schema-component/antd/remote-select/RemoteSelect.tsx b/packages/core/client/src/schema-component/antd/remote-select/RemoteSelect.tsx index 25dbacbe0..d2f2e2314 100644 --- a/packages/core/client/src/schema-component/antd/remote-select/RemoteSelect.tsx +++ b/packages/core/client/src/schema-component/antd/remote-select/RemoteSelect.tsx @@ -6,14 +6,12 @@ import flat from 'flat'; import _, { uniqBy } from 'lodash'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ResourceActionOptions, useRequest } from '../../../api-client'; -import { useBlockRequestContext } from '../../../block-provider/BlockProvider'; import { mergeFilter } from '../../../block-provider/SharedFilterProvider'; import { useCollection, useCollectionManager } from '../../../collection-manager'; import { getInnermostKeyAndValue } from '../../common/utils/uitls'; import { useCompile } from '../../hooks'; import { Select, defaultFieldNames } from '../select'; import { ReadPretty } from './ReadPretty'; -import { extractFilterfield, extractValuesByPattern, generatePattern, parseVariables } from './utils'; const EMPTY = 'N/A'; export type RemoteSelectProps

= SelectProps & { @@ -49,7 +47,6 @@ const InternalRemoteSelect = connect( const fieldSchema = useFieldSchema(); const isQuickAdd = fieldSchema['x-component-props']?.addMode === 'quickAdd'; const field = useField(); - const ctx = useBlockRequestContext(); const { getField } = useCollection(); const searchData = useRef(null); const { getCollectionJoinField, getInterface } = useCollectionManager(); @@ -123,70 +120,6 @@ const InternalRemoteSelect = connect( }, [targetField?.uiSchema, fieldNames], ); - const parseFilter = useCallback( - (rules) => { - if (!rules) { - return undefined; - } - if (typeof rules === 'string') { - return rules; - } - const type = Object.keys(rules)[0] || '$and'; - const conditions = rules[type]; - const results = []; - conditions?.forEach((c) => { - const jsonlogic = getInnermostKeyAndValue(c); - const regex = /{{(.*?)}}/; - const matches = jsonlogic.value?.match?.(regex); - if (!matches || (!matches[1].includes('$form') && !matches[1].includes('$iteration'))) { - results.push(c); - return; - } - const associationfield = extractFilterfield(matches[1]); - const filterCollectionField = getCollectionJoinField(`${ctx.props.collection}.${associationfield}`); - if (['o2m', 'm2m'].includes(filterCollectionField?.interface)) { - // 对多子表单 - const pattern = generatePattern(matches?.[1], associationfield); - const parseValue: any = extractValuesByPattern(flat(form.values), pattern); - const filters = parseValue.map((v) => { - return JSON.parse(JSON.stringify(c).replace(jsonlogic.value, v)); - }); - results.push({ $or: filters }); - } else { - const variablesCtx = { $form: form.values, $iteration: form.values }; - let str = matches?.[1]; - if (str.includes('$iteration')) { - const path = field.path.segments.concat([]); - path.pop(); - str = str.replace('$iteration.', `$iteration.${path.join('.')}.`); - } - const parseValue = parseVariables(str, variablesCtx); - if (Array.isArray(parseValue)) { - const filters = parseValue.map((v) => { - return JSON.parse(JSON.stringify(c).replace(jsonlogic.value, v)); - }); - results.push({ $or: filters }); - } else { - const filterObj = JSON.parse( - JSON.stringify(c).replace(jsonlogic.value, str.endsWith('id') ? parseValue ?? 0 : parseValue), - ); - results.push(filterObj); - } - } - }); - return { [type]: results }; - }, - [ctx.props?.collection, field.path.segments, form.values, getCollectionJoinField], - ); - - const filter = useMemo(() => { - const filterFromSchema = _.isString(fieldSchema?.['x-component-props']?.service?.params?.filter) - ? field.componentProps?.service?.params?.filter - : fieldSchema?.['x-component-props']?.service?.params?.filter; - - return mergeFilter([parseFilter(filterFromSchema) || service?.params?.filter]); - }, [field.componentProps?.service?.params?.filter, fieldSchema, parseFilter, service?.params?.filter]); - const { data, run, loading } = useRequest( { action: 'list', @@ -194,8 +127,7 @@ const InternalRemoteSelect = connect( params: { pageSize: 200, ...service?.params, - // search needs - filter, + filter: service?.params?.filter, }, }, { @@ -243,7 +175,7 @@ const InternalRemoteSelect = connect( }, } : {}, - filter, + service?.params?.filter, ]), }); searchData.current = search; diff --git a/packages/core/client/src/schema-component/antd/remote-select/utils.ts b/packages/core/client/src/schema-component/antd/remote-select/utils.ts deleted file mode 100644 index bb6ba7759..000000000 --- a/packages/core/client/src/schema-component/antd/remote-select/utils.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { get, isFunction } from 'lodash'; - -export const parseVariables = (str: string, ctx) => { - if (str) { - const result = get(ctx, str); - return isFunction(result) ? result() : result; - } else { - return str; - } -}; -export function extractFilterfield(str) { - const match = str.match(/^\$form\.([^.[\]]+)/); - if (match) { - return match[1]; - } - return null; -} - -export function extractValuesByPattern(obj, pattern) { - const regexPattern = new RegExp(pattern.replace(/\*/g, '\\d+')); - const result = []; - - for (const key in obj) { - if (regexPattern.test(key)) { - const value = obj[key]; - result.push(value); - } - } - - return result; -} -export function generatePattern(str, fieldName) { - const result = str.replace(`$form.${fieldName}.`, `${fieldName}.*.`); - return result; -}