diff --git a/packages/plugins/@hera/plugin-core/src/client/hooks/useFilterBlockActionProps.tsx b/packages/plugins/@hera/plugin-core/src/client/hooks/useFilterBlockActionProps.tsx index 7a658334b..07ef92def 100644 --- a/packages/plugins/@hera/plugin-core/src/client/hooks/useFilterBlockActionProps.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/hooks/useFilterBlockActionProps.tsx @@ -9,89 +9,45 @@ import { useFilterBlock, } from '@nocobase/client'; import flat from 'flat'; +import { hasDuplicateKeys } from '../utils'; export const removeNullCondition = (filter, fieldSchema?) => { const filterSchema = fieldSchema ? fieldSchema['x-filter-rules'] : ''; const filterSchemaItem = flat(filterSchema || ''); - const filterItem = {}; const items = flat(filter || {}); const values = {}; - let isFilterCustom = false; - if (filterSchema && (filterSchema.$and?.length || filterSchema.$or?.length)) { - for (const key in items) { - for (const filterItems in filterSchemaItem) { - if (key.includes(filterItems)) { - isFilterCustom = true; - break; - } - } - } - } + const isCustomFilter = filterSchema?.$and?.length || filterSchema?.$or?.length; + const isFilterCustom = isCustomFilter ? hasDuplicateKeys(items, filterSchemaItem) : false; if (!isFilterCustom) { - if (Object.keys(items).filter((item) => item.includes('custom')).length) { - if (filterSchema && (filterSchema.$and?.length || filterSchema.$or?.length)) { - for (const filterKey in filterSchemaItem) { - const match = filterSchemaItem[filterKey]?.slice(11, -2); - const collection = match?.split('.')[0]; - if (Object.keys(items).filter((item) => item.includes(collection)).length) { - for (const key in items) { - if (key.includes('custom')) { - if (key.includes(collection)) { - if (key.includes(match)) { - if (Object.keys(items[key]).length) { - filterSchemaItem[filterKey] = items[key]; - filterItem[match] = items[key]; - } - } - if (key.includes(collection)) delete items[key]; - else { - const value = items[key]; - if (value != null && !isEmpty(value)) { - values[key] = value; - } - } - } - } else { - values[key] = items[key]; - } - } - } else if (Object.keys(items).filter((item) => !item.includes('custom')).length) { - const filterItem = Object.keys(items).filter((item) => !item.includes('custom')); - filterItem.forEach((key) => { - values[key] = items[key]; - }); - } + if (isCustomFilter) { + for (const filterKey in filterSchemaItem) { + const match = filterSchemaItem[filterKey]?.slice(11, -2); + const collection = match?.split('.')[0]; + const filterItems = Object.keys(items).filter((item) => item.includes(collection))[0]; + if (filterItems) { + filterSchemaItem[filterKey] = items[filterItems]; } - for (const item in filterSchemaItem) { - for (const key in filterItem) { - if (filterSchemaItem[item].includes(key)) { - filterSchemaItem[item] = filterItem[key]; - } - } - if (filterSchemaItem[item].includes('$nFilter')) { - delete filterSchemaItem[item]; - } - } - const flatValue = flat.unflatten(values); - const flatFieldSchema = flat.unflatten(filterSchemaItem); - return { - $and: [flatValue, flatFieldSchema], - }; - } else { - for (const key in items) { - if (!key.includes('custom')) { - const value = items[key]; - if (value != null && !isEmpty(value)) { - values[key] = value; - } - } - } - return flat.unflatten(values); } + for (const item in items) { + if (!item.includes('custom')) { + values[item] = items[item]; + } + } + for (const item in filterSchemaItem) { + if (filterSchemaItem[item].includes('$nFilter')) { + delete filterSchemaItem[item]; + } + } + const flatValue = flat.unflatten(values); + const flatFieldSchema = flat.unflatten(filterSchemaItem); + flatValue['$and'] = flatValue['$and']?.filter(Boolean); + return { + $and: [flatValue, flatFieldSchema], + }; } else { for (const key in items) { const value = items[key]; - if (value != null && !isEmpty(value)) { + if (!key.includes('custom') && value != null && !isEmpty(value)) { values[key] = value; } } @@ -123,13 +79,11 @@ export const useFilterBlockActionProps = () => { getDataBlocks().map(async (block) => { const target = targets.find((target) => target.uid === block.uid); if (!target) return; - const param = block.service.params?.[0] || {}; for (const key in form.values) { if ( (typeof form.values[key] === 'object' && - !form.values[key]?.length && - !Object.keys(form.values[key]).length) || + (JSON.stringify(form.values[key]) === '{}' || JSON.stringify(form.values[key]) === '[]')) || !form.values[key] ) { delete form.values[key]; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-cascader/AssociationCascader.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/association-cascader/AssociationCascader.tsx index 2b323669e..38d272c7c 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/association-cascader/AssociationCascader.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-components/association-cascader/AssociationCascader.tsx @@ -5,6 +5,7 @@ import { useCollectionManager, useRequest } from '@nocobase/client'; import _ from 'lodash'; const AssociationCascader = connect((props) => { + const { fieldNames } = props; const fieldSchema = useFieldSchema(); const collection = fieldSchema['collectionName']; const associationField = props.associationField; @@ -36,8 +37,8 @@ const AssociationCascader = connect((props) => { return acc; }, {}) || {}; const options = Object.entries(dict).map(([key, values]) => ({ - name: key, - id: key, + [fieldNames.label]: key, + [fieldNames.value]: key, children: values.map((value) => ({ name: value, id: value })), })); return options; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/auto-complete/AutoComplete.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/auto-complete/AutoComplete.tsx index 8a8f6ec1d..b51712569 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/auto-complete/AutoComplete.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-components/auto-complete/AutoComplete.tsx @@ -1,25 +1,17 @@ import React, { useEffect, useState } from 'react'; import { AutoComplete as AntdAutoComplete } from 'antd'; -import { useFieldSchema, useForm } from '@formily/react'; +import { connect, useFieldSchema, useForm } from '@formily/react'; import { useAPIClient, useDesigner, useRequest } from '@nocobase/client'; import { useAsyncEffect } from 'ahooks'; +import { fuzzysearch } from '../../utils'; -export const AutoComplete = (props) => { +export const AutoComplete = connect((props) => { const fieldSchema = useFieldSchema(); - const comdef = fieldSchema['default']; - const { fieldNames } = fieldSchema['x-component-props']; - const fieldFilter = fieldSchema['x-component-props']['params']; + const fieldNames = props.fieldNames || fieldSchema['x-component-props'].fieldNames; + const fieldFilter = props.params; const [defultValue, setDefultValue] = useState([]); const api = useAPIClient(); const [options, setOptions] = useState([]); - const form = useForm(); - const autoValue = form.values['custom'] ? form.values['custom'][fieldSchema['collectionName']] : ''; - const [value, setValue] = useState(autoValue); - if (!autoValue && value) { - setValue(''); - setOptions(defultValue); - } - useAsyncEffect(async () => { const defultOptions = await api.request({ url: fieldSchema['collectionName'] + ':list', @@ -33,55 +25,28 @@ export const AutoComplete = (props) => { useEffect(() => { changLable(defultValue); - }, [fieldNames.label]); + }, [fieldNames?.label]); - useEffect(() => { - setValue(comdef); - }, [comdef]); const changLable = (defultOptions) => { if (defultOptions) { - const item = defultOptions.map((item) => { - item['label'] = item[fieldNames.label]; - item['value'] = item[fieldNames.value]; - return item; + const items = []; + defultOptions.forEach((item) => { + if (!items.filter((option) => option[fieldNames.value] === item[fieldNames.value]).length) { + items.push(item); + } }); - setDefultValue(item); - setOptions(item); - } - }; - if (!form.values['custom']) { - form.values['custom'] = {}; - } - const onSearch = (data) => { - if (data) { - const searchValue = defultValue.filter((item) => item.label.includes(data)); - if (searchValue.length) { - setOptions(searchValue); - } else { - setOptions([]); - } - const valueLabel = options.filter((item) => item.value === data)[0]; - if (valueLabel) { - form.values.custom[fieldSchema['collectionName']] = valueLabel.label; - setValue(valueLabel.label); - } else { - form.values.custom[fieldSchema['collectionName']] = data; - setValue(data); - } - } else { - form.values.custom[fieldSchema['collectionName']] = {}; - setValue(data); - setOptions(defultValue); + setDefultValue(items); + setOptions(items); } }; return ( fuzzysearch(inputValue, option[fieldNames.value].toString())} allowClear /> ); -}; +}); +AutoComplete.displayName = 'AutoComplete'; +export default AutoComplete; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-components/select/Select.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-components/select/Select.tsx index b21f14e38..66b146aac 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-components/select/Select.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-components/select/Select.tsx @@ -1,5 +1,5 @@ import { CloseCircleFilled, CloseOutlined, LoadingOutlined } from '@ant-design/icons'; -import { connect, mapProps, mapReadPretty, useFieldSchema } from '@formily/react'; +import { connect, mapProps, mapReadPretty, useFieldSchema, useForm } from '@formily/react'; import { isValid, toArr } from '@formily/shared'; import { isPlainObject } from '@nocobase/utils/client'; import type { SelectProps } from 'antd'; @@ -24,11 +24,10 @@ const ObjectSelect = (props: Props) => { const { value, options, onChange, fieldNames, mode, loading, rawOptions, defaultValue, ...others } = props; const [defoptions, setDefOptions] = useState(); const fieldSchema = useFieldSchema(); - const comDefult = fieldSchema.default; const collectionName = fieldSchema['collectionName']; - const filterField = fieldSchema['x-component-props']['params']; - const fieldName = fieldSchema['x-component-props'].fieldNames; + const filterField = others?.['params']; const api = useAPIClient(); + const form = useForm(); useAsyncEffect(async () => { if (collectionName) { const defValue = await api.request({ @@ -39,16 +38,13 @@ const ObjectSelect = (props: Props) => { }, }); const changOptions = defValue?.data?.data.map((value) => { - value['label'] = value[fieldName.label]; - value['value'] = value[fieldNames.label]; + value['label'] = value[fieldNames.label]; + value['value'] = value[fieldNames.value]; return value; }); setDefOptions(changOptions); } }, [filterField?.filter, collectionName]); - useEffect(() => { - onChange?.(comDefult); - }, [fieldSchema.default]); const toValue = (v: any) => { if (isEmptyObject(v)) { return; @@ -58,15 +54,12 @@ const ObjectSelect = (props: Props) => { .map((val) => { return isPlainObject(val) ? val[fieldNames.value] : val; }); - const currentOptions = getCurrentOptions(values, options, fieldNames)?.map((val) => { - if (collectionName) { - return val[fieldName.label]; - } else { - return { - label: val[fieldNames.label], - value: val[fieldNames.value], - }; - } + const filterOption = defoptions ? defoptions : options; + const currentOptions = getCurrentOptions(values, filterOption, fieldNames)?.map((val) => { + return { + label: val[fieldNames.label], + value: val[fieldNames.value], + }; }); if (['tags', 'multiple'].includes(mode) || props.multiple) { return currentOptions; @@ -83,7 +76,7 @@ const ObjectSelect = (props: Props) => { role="button" data-testid={`select-object-${mode || 'single'}`} value={toValue(value)} - defaultValue={toValue(defaultValue) || comDefult} + defaultValue={toValue(defaultValue)} allowClear={{ clearIcon: , }} @@ -106,17 +99,17 @@ const ObjectSelect = (props: Props) => { onChange={(changed) => { const current = getCurrentOptions( toArr(changed).map((v) => v.value), - rawOptions || options, + defoptions || rawOptions || options, fieldNames, ); - if (collectionName) { - onChange?.(changed['label']); + if (['tags', 'multiple'].includes(mode as string) || props.multiple) { + onChange?.(current); } else { - if (['tags', 'multiple'].includes(mode as string) || props.multiple) { - onChange?.(current); - } else { - onChange?.(current.shift() || null); - } + onChange?.(current.shift() || null); + } + if (collectionName) { + const name = fieldSchema['name'].toString().split('.')[1]; + form.values['custom'][name] = changed?.['value']; } }} mode={mode} diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx index 04bd9f2af..7f8f05e21 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx @@ -41,7 +41,6 @@ import { import _ from 'lodash'; import { SchemaSettingsRemove } from '../../schema-settings/SchemaSettingsRemove'; import { tval, useTranslation } from '../../locale'; -import { ContractsController } from 'packages/plugins/@hera/plugin-rental/dist/server/actions'; const FieldComponentProps: React.FC = observer( (props) => { @@ -296,7 +295,7 @@ export const FilterCustomItemInitializer: React.FC<{ ...(defaultSchema['x-component-props'] || {}), fieldNames: { label: 'name', - value: 'id', + value: 'name', }, associationField, }, diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsRemove.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsRemove.tsx index e13e6528c..904d02bbf 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsRemove.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-settings/SchemaSettingsRemove.tsx @@ -43,13 +43,7 @@ export const SchemaSettingsRemove: FC = (props) => { } await dn.remove(null, options); await confirm?.onOk?.(); - if (fieldSchema['collectionName']) { - delete form.values['custom'][fieldSchema['collectionName']]; - } else { - if (form.values['custom']?.[fieldName]) { - delete form.values['custom'][fieldName]; - } - } + delete form.values['custom'][fieldName]; for (const key in form.fields) { if (key.includes(name) && form.fields[key].title === title) { delete form.fields[key]; diff --git a/packages/plugins/@hera/plugin-core/src/client/schema-settings/index.tsx b/packages/plugins/@hera/plugin-core/src/client/schema-settings/index.tsx index 89776dae8..06c07a645 100644 --- a/packages/plugins/@hera/plugin-core/src/client/schema-settings/index.tsx +++ b/packages/plugins/@hera/plugin-core/src/client/schema-settings/index.tsx @@ -176,19 +176,15 @@ export const EditDefaultValue = () => { }, }, }} - onSubmit={(defaultValue) => { - let value; - if (defaultValue.default && !defaultValue.custom) { - value = defaultValue.default.value?.value ? defaultValue.default.value.value : defaultValue.default.value; - } else if (defaultValue.custom) { - value = defaultValue.custom[collectionName]; - } - field.setInitialValue(value); + onSubmit={({ default: { value } }) => { + field.setValue(value); fieldSchema['default'] = value; + fieldSchema['x-component-props']['defaultValue'] = value; dn.emit('patch', { schema: { 'x-uid': fieldSchema['x-uid'], - default: value, + ['x-component-props']: fieldSchema['x-component-props'], + default: fieldSchema.default, }, }); dn.refresh(); @@ -343,6 +339,7 @@ export const EditTitleField = () => { ...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'], ...field.componentProps.fieldNames, label, + value: label, }; fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; fieldSchema['x-component-props']['fieldNames'] = fieldNames; @@ -507,14 +504,11 @@ export const SchemaSettingComponent = () => { onChange={(mode) => { field.component = mode; fieldSchema['x-component'] = mode; - fieldSchema.default = ''; - const schema = { - ['x-uid']: fieldSchema['x-uid'], - ['x-component']: mode, - default: '', - }; void dn.emit('patch', { - schema, + schema: { + ['x-uid']: fieldSchema['x-uid'], + ['x-component']: fieldSchema['x-component'], + }, }); dn.refresh(); }} @@ -524,8 +518,8 @@ export const SchemaSettingComponent = () => { export const SchemaSettingCollection = () => { const fieldSchema = useFieldSchema(); - const field = useField(); const collections = useCollectionManager(); + const field = useField(); const options = collections?.dataSource['options']?.collections.map((value) => { return { label: value.title, @@ -540,16 +534,25 @@ export const SchemaSettingCollection = () => { options={options} value={fieldSchema['collectionName']} onChange={(name) => { + field.setValue(''); fieldSchema['collectionName'] = name; - fieldSchema['name'] = 'custom.' + name; fieldSchema.default = ''; - const schema = { - ['x-uid']: fieldSchema['x-uid'], - collectionName: name, - name: 'custom.' + name, - default: '', + fieldSchema['x-component-props'] = { + ...fieldSchema['x-component-props'], + fieldNames: { + label: 'name', + value: 'name', + }, + defaultValue: '', }; - void dn.emit('patch', { schema }); + void dn.emit('patch', { + schema: { + ['x-uid']: fieldSchema['x-uid'], + collectionName: fieldSchema['collectionName'], + ['x-component-props']: fieldSchema['x-component-props'], + default: '', + }, + }); dn.refresh(); }} /> diff --git a/packages/plugins/@hera/plugin-core/src/client/utils.ts b/packages/plugins/@hera/plugin-core/src/client/utils.ts new file mode 100644 index 000000000..a12cc5c1b --- /dev/null +++ b/packages/plugins/@hera/plugin-core/src/client/utils.ts @@ -0,0 +1,33 @@ +export function fuzzysearch(needle: string, haystack: string): boolean { + const hlen = haystack.length; + const nlen = needle.length; + if (nlen > hlen) { + return false; + } + if (nlen === hlen) { + return needle === haystack; + } + outer: for (let i = 0, j = 0; i < nlen; i++) { + const nch = needle.charCodeAt(i); + while (j < hlen) { + if (haystack.charCodeAt(j++) === nch) { + continue outer; + } + } + return false; + } + return true; +} + +// 数组去重 +const getArrayOfNoDuplicateValue = (arr: Array): Array => { + return [...new Set(arr)]; +}; + +// 判断两个对象是否包含有相同的key +export function hasDuplicateKeys(A: Object, B: Object): boolean { + const keysA = getArrayOfNoDuplicateValue(Object.keys(A)); + const keysB = getArrayOfNoDuplicateValue(Object.keys(B)); + const keysA_B = getArrayOfNoDuplicateValue([...keysA, ...keysB]); + return keysA_B.length < keysA.length + keysB.length; +} diff --git a/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_calc.sql b/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_calc.sql index 235057b5c..6eac37d24 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_calc.sql +++ b/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_calc.sql @@ -235,7 +235,7 @@ SELECT records r WHERE r.contract_id = COALESCE(main.id, c.id) - AND s.end_date + INTERVAL '1 day' >= r.date + AND s.end_date >= r.date ) AS records, ( SELECT