feat: support mobile field related (#941)
Reviewed-on: daoyoucloud/tachybase#941 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
9e63e1c392
commit
4f26e6e8c9
@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import { RecursionField } from '@tachybase/schema';
|
||||
import { useTabSearchAction } from './TabSearchAction';
|
||||
import { RecursionField, useFieldSchema } from '@tachybase/schema';
|
||||
import { useDesigner, useSchemaInitializerRender } from '@tachybase/client';
|
||||
import { isTabSearchCollapsibleInputItem } from '../utils';
|
||||
|
||||
export const TabSearch = () => {
|
||||
const { fieldSchema, Designer, render, filterProperties } = useTabSearchAction();
|
||||
const { fieldSchema, Designer, render, filterProperties } = useAction();
|
||||
return (
|
||||
<>
|
||||
<Designer />
|
||||
@ -12,3 +13,25 @@ export const TabSearch = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
const useAction = () => {
|
||||
const Designer = useDesigner();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']);
|
||||
|
||||
const filterProperties = (s) => {
|
||||
if (!isTabSearchCollapsibleInputItem(s['x-component'])) {
|
||||
return true;
|
||||
} else {
|
||||
// 保留第一个,如果进入这个判断一定有值,所以取0不会出错
|
||||
return (
|
||||
s === Object.values(s.parent.properties).filter((s) => isTabSearchCollapsibleInputItem(s['x-component']))[0]
|
||||
);
|
||||
}
|
||||
};
|
||||
return {
|
||||
fieldSchema,
|
||||
Designer,
|
||||
render,
|
||||
filterProperties,
|
||||
};
|
||||
};
|
||||
|
@ -1,26 +0,0 @@
|
||||
import { useDesigner, useSchemaInitializerRender } from '@tachybase/client';
|
||||
import { useFieldSchema } from '@tachybase/schema';
|
||||
import { isTabSearchCollapsibleInputItem } from '../utils';
|
||||
|
||||
export const useTabSearchAction = () => {
|
||||
const Designer = useDesigner();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']);
|
||||
|
||||
const filterProperties = (s) => {
|
||||
if (!isTabSearchCollapsibleInputItem(s['x-component'])) {
|
||||
return true;
|
||||
} else {
|
||||
// 保留第一个,如果进入这个判断一定有值,所以取0不会出错
|
||||
return (
|
||||
s === Object.values(s.parent.properties).filter((s) => isTabSearchCollapsibleInputItem(s['x-component']))[0]
|
||||
);
|
||||
}
|
||||
};
|
||||
return {
|
||||
fieldSchema,
|
||||
Designer,
|
||||
render,
|
||||
filterProperties,
|
||||
};
|
||||
};
|
@ -23,7 +23,7 @@ export const ISelect = (props) => {
|
||||
}}
|
||||
>
|
||||
<span style={{ textAlign: 'center', width: '100%' }}>
|
||||
{options.find((option) => option.value === customLabelKey).label}
|
||||
{options.find((option) => option.value === customLabelKey)?.label}
|
||||
</span>
|
||||
<DownOutline />
|
||||
{options.length <= 5 ? (
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { SortableItem, withDynamicSchemaProps } from '@tachybase/client';
|
||||
import { ConfigProvider, Menu } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTabSearchFieldItemAction } from './TabSearchFieldItemAction';
|
||||
import { useAction } from './TabSearchFieldMItem';
|
||||
|
||||
export const TabSearchFieldItem = withDynamicSchemaProps(
|
||||
(props) => {
|
||||
const { collectionField, Designer, items, onSelect } = useTabSearchFieldItemAction(props);
|
||||
const { collectionField, Designer, items, onSelect } = useAction(props);
|
||||
|
||||
if (!collectionField) {
|
||||
return null;
|
||||
|
@ -1,48 +0,0 @@
|
||||
import React from 'react';
|
||||
import { MenuProps } from 'antd';
|
||||
import { useCollection, useDesigner } from '@tachybase/client';
|
||||
import { useFieldSchema } from '@tachybase/schema';
|
||||
import { useTranslation } from '../../../../../../locale';
|
||||
|
||||
export const useTabSearchFieldItemAction = (props) => {
|
||||
const { list, onSelected, valueKey: _valueKey, labelKey: _labelKey, filterKey } = props;
|
||||
const { t } = useTranslation();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const c = useCollection();
|
||||
const collectionField = React.useMemo(
|
||||
() => c?.getField(fieldSchema['fieldName'] as any),
|
||||
[c, fieldSchema['fieldName']],
|
||||
);
|
||||
const Designer = useDesigner();
|
||||
const valueKey = _valueKey || collectionField?.targetKey || 'id';
|
||||
const labelKey = _labelKey || fieldSchema['x-component-props']?.fieldNames?.label || valueKey;
|
||||
const onSelect = (itemKey) => {
|
||||
const key = itemKey.keyPath?.[0] || itemKey;
|
||||
onSelected([key], filterKey);
|
||||
};
|
||||
const fieldNames = {
|
||||
title: labelKey || valueKey,
|
||||
key: valueKey,
|
||||
};
|
||||
const items: MenuProps['items'] = [];
|
||||
|
||||
if (list?.length) {
|
||||
list.forEach((value) => {
|
||||
items.push({
|
||||
label: value[fieldNames.title],
|
||||
key: value[fieldNames.key],
|
||||
});
|
||||
});
|
||||
}
|
||||
items?.unshift({
|
||||
label: t('all'),
|
||||
key: 'all',
|
||||
});
|
||||
|
||||
return {
|
||||
collectionField,
|
||||
Designer,
|
||||
items,
|
||||
onSelect,
|
||||
};
|
||||
};
|
@ -8,20 +8,25 @@ import { canBeOptionalField } from '../../utils';
|
||||
export const useTabSearchFieldItemProps = () => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const collection = useCollection();
|
||||
const optionalFieldList = useOptionalFieldList();
|
||||
const currentCollectionName = fieldSchema['x-component-props']?.currentCollection || collection?.name;
|
||||
const optionalFieldList = useOptionalFieldList(currentCollectionName);
|
||||
|
||||
const cm = useCollectionManager();
|
||||
const collectionField = useMemo(
|
||||
() => collection?.getField(fieldSchema['fieldName'] as any),
|
||||
[collection, fieldSchema['fieldName']],
|
||||
);
|
||||
|
||||
const { onSelected } = useTabSearchCollapsibleInputItem();
|
||||
const result = { list: null, valueKey: '', labelKey: '', filterKey: '' };
|
||||
if (!collection) return;
|
||||
result.valueKey = collectionField?.target ? cm.getCollection(collectionField.target)?.getPrimaryKey() : 'id';
|
||||
result.labelKey = fieldSchema['x-component-props']?.fieldNames?.label || result.valueKey;
|
||||
const fieldInterface = fieldSchema['x-component-props'].interface;
|
||||
const fieldInterface = fieldSchema['x-component-props']?.interface;
|
||||
if (canBeOptionalField(fieldInterface)) {
|
||||
const field = optionalFieldList.find((field) => field.name === fieldSchema['fieldName']);
|
||||
const field = optionalFieldList.find(
|
||||
(field) => field.name === fieldSchema['fieldName'].split('.').findLast(Boolean),
|
||||
);
|
||||
const operatorMap = {
|
||||
select: '$in',
|
||||
multipleSelect: '$anyOf',
|
||||
@ -29,12 +34,15 @@ export const useTabSearchFieldItemProps = () => {
|
||||
radioGroup: '$in',
|
||||
};
|
||||
|
||||
fieldSchema['fieldName'];
|
||||
|
||||
const _list = field?.uiSchema?.enum || [];
|
||||
result.valueKey = 'value';
|
||||
result.labelKey = 'label';
|
||||
result.list = _list;
|
||||
result.filterKey = `${field.name}.${operatorMap[field.interface]}`;
|
||||
result.filterKey = `${fieldSchema['fieldName']}.${operatorMap[field?.interface]}`;
|
||||
}
|
||||
|
||||
return {
|
||||
list: result.list,
|
||||
valueKey: result.valueKey,
|
||||
@ -44,8 +52,9 @@ export const useTabSearchFieldItemProps = () => {
|
||||
};
|
||||
};
|
||||
|
||||
const useOptionalFieldList = () => {
|
||||
const collection = useCollection();
|
||||
const useOptionalFieldList = (collectionName) => {
|
||||
const cm = useCollectionManager();
|
||||
const collection = cm.getCollection(collectionName);
|
||||
const currentFields = collection?.fields ?? [];
|
||||
return currentFields.filter((field) => canBeOptionalField(field.interface) && field.uiSchema.enum);
|
||||
};
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { SortableItem, withDynamicSchemaProps } from '@tachybase/client';
|
||||
import React from 'react';
|
||||
import { useTabSearchFieldItemAction } from './TabSearchFieldItemAction';
|
||||
import { SortableItem, useCollection, useDesigner, withDynamicSchemaProps } from '@tachybase/client';
|
||||
import { useFieldSchema } from '@tachybase/schema';
|
||||
import { MenuProps } from 'antd';
|
||||
import { Tabs } from 'antd-mobile';
|
||||
import React from 'react';
|
||||
import { useTranslation } from '../../../../../../locale';
|
||||
|
||||
export const TabSearchFieldMItem = withDynamicSchemaProps(
|
||||
(props) => {
|
||||
const { collectionField, Designer, items, onSelect } = useTabSearchFieldItemAction(props);
|
||||
const { collectionField, Designer, items, onSelect } = useAction(props);
|
||||
|
||||
if (!collectionField) {
|
||||
return null;
|
||||
@ -15,7 +17,7 @@ export const TabSearchFieldMItem = withDynamicSchemaProps(
|
||||
<SortableItem>
|
||||
<Designer />
|
||||
<Tabs style={{ '--title-font-size': '12px', color: '#a5a5a5' }} onChange={onSelect}>
|
||||
{items.map((value) => (
|
||||
{items.map((value: any) => (
|
||||
<Tabs.Tab title={value.label} key={value.key} />
|
||||
))}
|
||||
</Tabs>
|
||||
@ -24,3 +26,46 @@ export const TabSearchFieldMItem = withDynamicSchemaProps(
|
||||
},
|
||||
{ displayName: 'TabSearchFieldItem' },
|
||||
);
|
||||
|
||||
export const useAction = (props) => {
|
||||
const { list, onSelected, valueKey: _valueKey, labelKey: _labelKey, filterKey } = props;
|
||||
const { t } = useTranslation();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const c = useCollection();
|
||||
const collectionField = React.useMemo(
|
||||
() => c?.getField(fieldSchema['fieldName'] as any),
|
||||
[c, fieldSchema['fieldName']],
|
||||
);
|
||||
const Designer = useDesigner();
|
||||
const valueKey = _valueKey || collectionField?.targetKey || 'id';
|
||||
const labelKey = _labelKey || fieldSchema['x-component-props']?.fieldNames?.label || valueKey;
|
||||
const onSelect = (itemKey) => {
|
||||
const key = itemKey.keyPath?.[0] || itemKey;
|
||||
onSelected([key], filterKey);
|
||||
};
|
||||
const fieldNames = {
|
||||
title: labelKey || valueKey,
|
||||
key: valueKey,
|
||||
};
|
||||
const items: MenuProps['items'] = [];
|
||||
|
||||
if (list?.length) {
|
||||
list.forEach((value) => {
|
||||
items.push({
|
||||
label: value[fieldNames.title],
|
||||
key: value[fieldNames.key],
|
||||
});
|
||||
});
|
||||
}
|
||||
items?.unshift({
|
||||
label: t('all'),
|
||||
key: 'all',
|
||||
});
|
||||
|
||||
return {
|
||||
collectionField,
|
||||
Designer,
|
||||
items,
|
||||
onSelect,
|
||||
};
|
||||
};
|
||||
|
@ -1,12 +1,6 @@
|
||||
// 关系字段类型
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import {
|
||||
useCollection,
|
||||
useCollectionManager,
|
||||
useDataSourceHeaders,
|
||||
useFilterBlock,
|
||||
useRequest,
|
||||
} from '@tachybase/client';
|
||||
import { useCollection, useCollectionManager, useDataSourceHeaders, useRequest } from '@tachybase/client';
|
||||
import { useField, useFieldSchema } from '@tachybase/schema';
|
||||
import _ from 'lodash';
|
||||
import { useTabSearchCollapsibleInputItem } from './hooks';
|
||||
@ -24,7 +18,10 @@ export const useTabSearchFieldItemRelatedProps = () => {
|
||||
() => collection?.getField(fieldSchema['fieldName'] as any),
|
||||
[collection, fieldSchema['fieldName']],
|
||||
);
|
||||
const collectionFieldName = collectionField?.name;
|
||||
const currentCollectionName = fieldSchema['x-component-props'].currentCollection;
|
||||
const collectionFieldName =
|
||||
currentCollectionName === collection?.name ? collectionField?.name : fieldSchema['fieldName'];
|
||||
|
||||
const fieldInterface = fieldSchema['x-component-props'].interface;
|
||||
const result = { list: null, valueKey: '', labelKey: '', filterKey: '' };
|
||||
const { onSelected } = useTabSearchCollapsibleInputItem();
|
||||
|
@ -1,3 +1,2 @@
|
||||
export * from './TabSearch';
|
||||
export * from './TabSearchAction';
|
||||
export * from './field-item';
|
||||
|
@ -0,0 +1,371 @@
|
||||
import {
|
||||
CollectionFieldOptions_deprecated,
|
||||
CollectionManager,
|
||||
FieldOptions,
|
||||
InheritanceCollectionMixin,
|
||||
SchemaInitializerChildren,
|
||||
SchemaInitializerItemType,
|
||||
isAssocField,
|
||||
useCollection,
|
||||
useCollectionManager,
|
||||
useDataSource,
|
||||
useDataSourceManager,
|
||||
} from '@tachybase/client';
|
||||
import React, { useCallback } from 'react';
|
||||
import { tval } from '../../../../../locale';
|
||||
import { canBeDataField, canBeOptionalField, canBeRelatedField, canBeSearchField } from '../utils';
|
||||
import { useIsMobile } from '../components/field-item/hooks';
|
||||
|
||||
interface ItemInterface {
|
||||
field: FieldOptions;
|
||||
label: string;
|
||||
schemaName: string;
|
||||
collectionName: string;
|
||||
getCollectionFields;
|
||||
processedCollections: string[];
|
||||
collectionManager: CollectionManager;
|
||||
currentCollection?: string;
|
||||
}
|
||||
|
||||
export const TabSearchAssociatedFields = () => {
|
||||
// NOTE: 这里的 useAssociationFields 借鉴的是 useFilterAssociatedFormItemInitializerFields
|
||||
const associationFieldsText = useAssociationFields('textField');
|
||||
const associationFieldsChoice = useAssociationFields('choiceField');
|
||||
// 这里的 children, 后续实际不是用来渲染的, 是个普通的 props 字段
|
||||
// XXX: refactor, 不好的设计, 用 children 来做普通 props 的数据传递
|
||||
const children: any[] = [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: tval('Display association Textfields', false),
|
||||
children: associationFieldsText,
|
||||
},
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: tval('Display association Choicefields', false),
|
||||
children: associationFieldsChoice,
|
||||
},
|
||||
];
|
||||
|
||||
return <SchemaInitializerChildren>{children}</SchemaInitializerChildren>;
|
||||
};
|
||||
|
||||
function useAssociationFields(type: 'textField' | 'choiceField') {
|
||||
const collection = useCollection<{ allFields: FieldOptions[] }>();
|
||||
|
||||
const { name, allFields } = collection || {};
|
||||
|
||||
const { getCollectionFields } = useCollectionFields_custom();
|
||||
const isMobield = useIsMobile();
|
||||
|
||||
const cm = useCollectionManager();
|
||||
|
||||
const filteredAssociationFields = allFields?.filter(({ interface: _interface }) => {
|
||||
return canBeRelatedField(_interface);
|
||||
});
|
||||
|
||||
const paramsItem = {
|
||||
collectionName: name,
|
||||
getCollectionFields,
|
||||
processedCollections: [],
|
||||
collectionManager: cm,
|
||||
};
|
||||
|
||||
let mappedAssociationFields = [];
|
||||
if (type === 'textField') {
|
||||
mappedAssociationFields = filteredAssociationFields.map((field) => {
|
||||
// 将关联表字段转换为 schema 树
|
||||
const label = canBeRelatedField(field.interface)
|
||||
? cm.getCollection(`${name}.${field.name}`)?.titleField
|
||||
: field.name;
|
||||
|
||||
const result = getItemInput(
|
||||
{
|
||||
...paramsItem,
|
||||
field,
|
||||
label,
|
||||
schemaName: field.name,
|
||||
},
|
||||
isMobield,
|
||||
);
|
||||
return result;
|
||||
});
|
||||
} else if (type === 'choiceField') {
|
||||
mappedAssociationFields = filteredAssociationFields.map((field) => {
|
||||
// 将关联表字段转换为 schema 树
|
||||
const label = cm.getCollection(field.target)?.getPrimaryKey() || 'id';
|
||||
const result = getItemChoice(
|
||||
{
|
||||
...paramsItem,
|
||||
field,
|
||||
label,
|
||||
schemaName: field.name,
|
||||
},
|
||||
isMobield,
|
||||
);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
const associationFields = mappedAssociationFields.filter(Boolean);
|
||||
return associationFields;
|
||||
}
|
||||
|
||||
// NOTE: 递归生成 schema 树
|
||||
function getItemInput(params: ItemInterface, isMobield?: boolean) {
|
||||
const {
|
||||
field,
|
||||
label,
|
||||
schemaName,
|
||||
collectionName,
|
||||
getCollectionFields,
|
||||
processedCollections,
|
||||
collectionManager: cm,
|
||||
} = params;
|
||||
const { interface: _interface } = field;
|
||||
const isCanBeOptional = canBeOptionalField(_interface);
|
||||
const isCanBeRelated = canBeRelatedField(_interface);
|
||||
|
||||
// NOTE: 目前只支持 m2o 和 obo 关联
|
||||
if (['m2o', 'obo'].includes(_interface)) {
|
||||
if (processedCollections.includes(field.target)) return null;
|
||||
const subFields = getCollectionFields(field.target);
|
||||
return {
|
||||
type: 'subMenu',
|
||||
name: field.uiSchema?.title,
|
||||
title: field.uiSchema?.title,
|
||||
// 将所有的关联字段排在最后
|
||||
children: subFields
|
||||
.toSorted((a, b) => +Object.hasOwn(a, 'target') - +Object.hasOwn(b, 'target'))
|
||||
.map((subField) => {
|
||||
const label = canBeRelatedField(field.interface)
|
||||
? cm.getCollection(`${name}.${field.name}`)?.titleField
|
||||
: field.name;
|
||||
return getItemInput({
|
||||
field: subField,
|
||||
label,
|
||||
schemaName: `${schemaName}.${subField.name}`,
|
||||
collectionName: collectionName,
|
||||
getCollectionFields: getCollectionFields,
|
||||
processedCollections: [...processedCollections, field.target],
|
||||
collectionManager: cm,
|
||||
});
|
||||
})
|
||||
.filter(Boolean),
|
||||
} as SchemaInitializerItemType;
|
||||
}
|
||||
// 然后排除其他类型的关联字段
|
||||
if (isAssocField(field)) return null;
|
||||
|
||||
if (!(canBeSearchField(_interface) && !canBeRelatedField(_interface) && !canBeDataField(_interface))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 制造 schema
|
||||
// const schema = ;
|
||||
|
||||
// {
|
||||
// type: 'itemGroup',
|
||||
// name: 'textFields',
|
||||
// title: tval('Text fields'),
|
||||
// useChildren: useChildrenTextField,
|
||||
// },
|
||||
// {
|
||||
// type: 'itemGroup',
|
||||
// name: 'choicesFields',
|
||||
// title: tval('Choices fields'),
|
||||
// useChildren: useChildrenChoiceField,
|
||||
// },
|
||||
|
||||
return {
|
||||
// type: 'item',
|
||||
name: field.uiSchema?.title || field.name,
|
||||
title: field.uiSchema?.title || field.name,
|
||||
Component: 'TabSearchFieldSchemaInitializerGadget',
|
||||
// remove: removeGridFormItem,
|
||||
schema: {
|
||||
// type: 'string',
|
||||
type: 'void',
|
||||
name: schemaName,
|
||||
title: `${field.uiSchema?.title}`,
|
||||
fieldName: `${schemaName}`,
|
||||
// 'x-designer': 'FormItem.FilterFormDesigner',
|
||||
// 'x-designer-props': {
|
||||
// // 在 useOperatorList 中使用,用于获取对应的操作符列表
|
||||
// interface: field.interface,
|
||||
// },
|
||||
'x-toolbar': 'CollapseItemSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:TabSearchItem',
|
||||
'x-collection-field': `${collectionName}.${schemaName}`,
|
||||
// 'x-decorator': 'FormItem',
|
||||
// 'x-component': 'CollectionField',
|
||||
'x-component': matchTruthValue({
|
||||
['TabSearchCollapsibleInputMItem']: isMobield,
|
||||
['TabSearchCollapsibleInputItem']: !isMobield,
|
||||
}),
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label,
|
||||
},
|
||||
interface: field.interface,
|
||||
collectionName: collectionName,
|
||||
correlation: field.name,
|
||||
},
|
||||
'x-use-component-props': matchTruthValue({
|
||||
['useTabSearchFieldItemProps']: isCanBeOptional,
|
||||
['useTabSearchFieldItemRelatedProps']: isCanBeRelated,
|
||||
}),
|
||||
|
||||
// 'x-read-pretty': false,
|
||||
},
|
||||
} as SchemaInitializerItemType;
|
||||
}
|
||||
// NOTE: 递归生成 schema 树
|
||||
function getItemChoice(params: ItemInterface, isMobield?: boolean) {
|
||||
const {
|
||||
field,
|
||||
label,
|
||||
schemaName,
|
||||
collectionName,
|
||||
getCollectionFields,
|
||||
processedCollections,
|
||||
collectionManager: cm,
|
||||
currentCollection,
|
||||
} = params;
|
||||
|
||||
const { interface: _interface } = field;
|
||||
const isCanBeOptional = canBeOptionalField(_interface);
|
||||
const isCanBeRelated = canBeRelatedField(_interface);
|
||||
|
||||
// NOTE: 目前只支持 m2o 和 obo 关联
|
||||
if (['m2o', 'obo'].includes(_interface) && !field.choiceTag) {
|
||||
if (processedCollections.includes(field.target)) return null;
|
||||
const subFields = getCollectionFields(field.target);
|
||||
return {
|
||||
type: 'subMenu',
|
||||
name: field.uiSchema?.title,
|
||||
title: field.uiSchema?.title,
|
||||
// 将所有的关联字段排在最后
|
||||
children: subFields
|
||||
.reduce((acc, subField) => {
|
||||
if (Object.hasOwn(subField, 'target') && ['m2o', 'obo'].includes(subField.interface)) {
|
||||
// NOTE: 将当前的关联字段复制一份, 作为当前层级的选择展示,并打上选择标记 choiceTag
|
||||
acc.push({
|
||||
...subField,
|
||||
choiceTag: true,
|
||||
});
|
||||
}
|
||||
return [...acc, subField];
|
||||
}, [])
|
||||
// NOTE: 排序规则: 1. 关联字段放最后 2. 选择复制字段次之 3. 其他字段置前
|
||||
.toSorted(
|
||||
(a, b) =>
|
||||
+Object.hasOwn(a, 'target') * 2 -
|
||||
+Object.hasOwn(a, 'choiceTag') -
|
||||
+Object.hasOwn(b, 'target') * 2 -
|
||||
+Object.hasOwn(b, 'choiceTag'),
|
||||
)
|
||||
.map((subField) => {
|
||||
const label = cm.getCollection(field.target)?.getPrimaryKey() || 'id';
|
||||
return getItemChoice({
|
||||
field: subField,
|
||||
label,
|
||||
schemaName: `${schemaName}.${subField.name}`,
|
||||
collectionName: collectionName,
|
||||
getCollectionFields: getCollectionFields,
|
||||
processedCollections: [...processedCollections, field.target],
|
||||
collectionManager: cm,
|
||||
currentCollection: field.target,
|
||||
});
|
||||
})
|
||||
.filter(Boolean),
|
||||
} as SchemaInitializerItemType;
|
||||
}
|
||||
|
||||
// 然后排除其他类型的关联字段, 并且不要删除打上选择标记 choiceTag 的关联字段
|
||||
if (isAssocField(field) && !field.choiceTag) return null;
|
||||
|
||||
if (!canBeOptionalField(_interface) && !canBeRelatedField(_interface)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'item',
|
||||
name: field.uiSchema?.title || field.name,
|
||||
title: field.uiSchema?.title || field.name,
|
||||
Component: 'TabSearchFieldSchemaInitializerGadget',
|
||||
// remove: removeGridFormItem,
|
||||
schema: {
|
||||
// type: 'string',
|
||||
type: 'void',
|
||||
name: schemaName,
|
||||
title: `${field.uiSchema?.title}`,
|
||||
fieldName: `${schemaName}`,
|
||||
// 'x-designer': 'FormItem.FilterFormDesigner',
|
||||
// 'x-designer-props': {
|
||||
// // 在 useOperatorList 中使用,用于获取对应的操作符列表
|
||||
// interface: field.interface,
|
||||
// },
|
||||
'x-toolbar': 'CollapseItemSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:TabSearchItem',
|
||||
// 'x-read-pretty': false,
|
||||
'x-collection-field': `${collectionName}.${schemaName}`,
|
||||
// 'x-decorator': 'FormItem',
|
||||
// 'x-component': 'CollectionField',
|
||||
'x-component': matchTruthValue({
|
||||
['TabSearchFieldMItem']: isMobield,
|
||||
['TabSearchFieldItem']: !isMobield,
|
||||
}),
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label,
|
||||
},
|
||||
interface: field.interface,
|
||||
collectionName: collectionName,
|
||||
correlation: field.name,
|
||||
currentCollection,
|
||||
},
|
||||
'x-use-component-props': matchTruthValue({
|
||||
['useTabSearchFieldItemProps']: isCanBeOptional,
|
||||
['useTabSearchFieldItemRelatedProps']: isCanBeRelated,
|
||||
}),
|
||||
},
|
||||
} as SchemaInitializerItemType;
|
||||
}
|
||||
|
||||
// XXX: 为了不使用标记废弃的 api, 模拟复制原方法实现; 参考 useCollectionManager_deprecated
|
||||
function useCollectionFields_custom(dataSourceName?: string) {
|
||||
const dm = useDataSourceManager();
|
||||
const cm = useCollectionManager();
|
||||
const dataSource = useDataSource();
|
||||
const dataSourceNameValue = dataSourceName || dataSource?.key || undefined;
|
||||
|
||||
const getCm = useCallback(
|
||||
(dataSource?: string) => {
|
||||
if (cm && !dataSource) {
|
||||
return cm;
|
||||
} else {
|
||||
return dm?.getDataSource(dataSource || dataSourceNameValue)?.collectionManager;
|
||||
}
|
||||
},
|
||||
[cm, dm, dataSourceNameValue],
|
||||
);
|
||||
|
||||
const getCollectionFields = useCallback(
|
||||
(name: any, customDataSource?: string): CollectionFieldOptions_deprecated[] => {
|
||||
if (!name) return [];
|
||||
const collection = getCm(customDataSource)?.getCollection<InheritanceCollectionMixin>(name);
|
||||
return collection?.getAllFields?.() || collection?.getFields() || [];
|
||||
},
|
||||
[dm, getCm],
|
||||
);
|
||||
|
||||
return {
|
||||
getCollectionFields,
|
||||
};
|
||||
}
|
||||
|
||||
function matchTruthValue(obj: Record<string, boolean>): string {
|
||||
const result = Object.entries(obj).find(([, value]) => value);
|
||||
const [key] = result || [];
|
||||
return key;
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import { DataBlockInitializer, useSchemaInitializer, useSchemaInitializerItem } from '@tachybase/client';
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import { DataBlockInitializer, Icon, useSchemaInitializer, useSchemaInitializerItem } from '@tachybase/client';
|
||||
import { createTabSearchBlockSchema } from '../create/createTabSearchBlockSchema';
|
||||
|
||||
export const TabSearchBlockInitializer = (props) => {
|
||||
|
@ -1,78 +1,100 @@
|
||||
import { SchemaInitializer, SchemaInitializerItemType, useCollection, useCollectionManager } from '@tachybase/client';
|
||||
import _ from 'lodash';
|
||||
import { canBeDataField, canBeOptionalField, canBeRelatedField, canBeSearchField } from '../utils';
|
||||
import { createTabSearchItemSchema } from '../create/createTabSearchItemSchema';
|
||||
import { useIsMobile } from '../components/field-item/hooks';
|
||||
import { SchemaInitializer, useCollection, useCollectionManager } from '@tachybase/client';
|
||||
import { tval } from '../../../../../locale';
|
||||
|
||||
const textFieldsItem: SchemaInitializerItemType<any> = {
|
||||
name: 'textFields',
|
||||
type: 'itemGroup',
|
||||
title: tval('Text fields'),
|
||||
useChildren() {
|
||||
const collection = useCollection();
|
||||
const associatedFields = collection.fields;
|
||||
const cm = useCollectionManager();
|
||||
const isMobield = useIsMobile();
|
||||
const itemComponent = isMobield ? 'TabSearchCollapsibleInputMItem' : 'TabSearchCollapsibleInputItem';
|
||||
const children = associatedFields
|
||||
.map((field) => {
|
||||
if (
|
||||
!field['isForeignKey'] &&
|
||||
(canBeSearchField(field.interface) || canBeRelatedField(field.interface) || canBeDataField(field.interface))
|
||||
) {
|
||||
const label = canBeRelatedField(field.interface)
|
||||
? cm.getCollection(`${collection.name}.${field.name}`).titleField
|
||||
: field.name;
|
||||
return createTabSearchItemSchema({ field, itemComponent, label, collection, type: 'text' });
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
return children;
|
||||
},
|
||||
};
|
||||
|
||||
const choicesFieldsItem: SchemaInitializerItemType<any> = {
|
||||
name: 'choicesFields',
|
||||
type: 'itemGroup',
|
||||
title: tval('Choices fields'),
|
||||
useChildren() {
|
||||
const collection = useCollection();
|
||||
const optionalList = collection.fields;
|
||||
const cm = useCollectionManager();
|
||||
const isMobield = useIsMobile();
|
||||
const itemComponent = isMobield ? 'TabSearchFieldMItem' : 'TabSearchFieldItem';
|
||||
const optionalChildren = optionalList
|
||||
.map((field) => {
|
||||
const label = cm.getCollection(field.target)?.getPrimaryKey() || 'id';
|
||||
if (canBeOptionalField(field.interface)) {
|
||||
return createTabSearchItemSchema({
|
||||
field,
|
||||
itemComponent,
|
||||
label,
|
||||
itemUseProps: 'useTabSearchFieldItemProps',
|
||||
type: 'choices',
|
||||
});
|
||||
} else if (canBeRelatedField(field.interface)) {
|
||||
return createTabSearchItemSchema({
|
||||
field,
|
||||
itemComponent,
|
||||
label,
|
||||
itemUseProps: 'useTabSearchFieldItemRelatedProps',
|
||||
type: 'choices',
|
||||
});
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
return optionalChildren;
|
||||
},
|
||||
};
|
||||
import { useIsMobile } from '../components/field-item/hooks';
|
||||
import { createTabSearchItemSchema } from '../create/createTabSearchItemSchema';
|
||||
import { canBeDataField, canBeOptionalField, canBeRelatedField, canBeSearchField } from '../utils';
|
||||
import { TabSearchAssociatedFields } from './TabSearchAssociated.fields';
|
||||
|
||||
export const TabSearchFieldSchemaInitializer = new SchemaInitializer({
|
||||
name: 'tabSearch:configureFields',
|
||||
style: { marginTop: 16 },
|
||||
icon: 'SettingOutlined',
|
||||
title: tval('Configure fields'),
|
||||
items: [textFieldsItem, choicesFieldsItem],
|
||||
items: [
|
||||
{
|
||||
name: 'textFields',
|
||||
type: 'itemGroup',
|
||||
title: tval('Text fields'),
|
||||
useChildren: useChildrenTextField,
|
||||
},
|
||||
{
|
||||
name: 'choicesFields',
|
||||
type: 'itemGroup',
|
||||
title: tval('Choices fields'),
|
||||
useChildren: useChildrenChoiceField,
|
||||
},
|
||||
{
|
||||
name: 'divider',
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
name: 'associationFields',
|
||||
Component: TabSearchAssociatedFields,
|
||||
},
|
||||
{
|
||||
name: 'divider',
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
title: tval('Add text', false),
|
||||
Component: 'MarkdownFormItemInitializer',
|
||||
name: 'addText',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 文本字段
|
||||
function useChildrenTextField() {
|
||||
const collection = useCollection();
|
||||
const associatedFields = collection.fields;
|
||||
const cm = useCollectionManager();
|
||||
const isMobield = useIsMobile();
|
||||
const itemComponent = isMobield ? 'TabSearchCollapsibleInputMItem' : 'TabSearchCollapsibleInputItem';
|
||||
const children = associatedFields
|
||||
.map((field) => {
|
||||
if (
|
||||
!field['isForeignKey'] &&
|
||||
(canBeSearchField(field.interface) || canBeRelatedField(field.interface) || canBeDataField(field.interface))
|
||||
) {
|
||||
const label = canBeRelatedField(field.interface)
|
||||
? cm.getCollection(`${collection.name}.${field.name}`).titleField
|
||||
: field.name;
|
||||
return createTabSearchItemSchema({ field, itemComponent, label, collection, type: 'text' });
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
return children;
|
||||
}
|
||||
|
||||
// 选择字段
|
||||
function useChildrenChoiceField() {
|
||||
const collection = useCollection();
|
||||
const optionalList = collection.fields;
|
||||
const cm = useCollectionManager();
|
||||
const isMobield = useIsMobile();
|
||||
const itemComponent = isMobield ? 'TabSearchFieldMItem' : 'TabSearchFieldItem';
|
||||
const optionalChildren = optionalList
|
||||
.map((field) => {
|
||||
const label = cm.getCollection(field.target)?.getPrimaryKey() || 'id';
|
||||
if (canBeOptionalField(field.interface)) {
|
||||
return createTabSearchItemSchema({
|
||||
field,
|
||||
itemComponent,
|
||||
label,
|
||||
itemUseProps: 'useTabSearchFieldItemProps',
|
||||
type: 'choices',
|
||||
});
|
||||
} else if (canBeRelatedField(field.interface)) {
|
||||
return createTabSearchItemSchema({
|
||||
field,
|
||||
itemComponent,
|
||||
label,
|
||||
itemUseProps: 'useTabSearchFieldItemRelatedProps',
|
||||
type: 'choices',
|
||||
});
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
return optionalChildren;
|
||||
}
|
||||
|
@ -71,11 +71,14 @@ export const TabSearchItemFieldSettings = new SchemaSettings({
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { t } = useTranslation();
|
||||
const cm = useCollectionManager();
|
||||
const c = useCollection();
|
||||
const fieldCollection = fieldSchema['x-component-props']?.['collectionName'];
|
||||
const correlation = fieldSchema['x-component-props']?.['correlation'];
|
||||
const collection = useCollection();
|
||||
|
||||
const propsInSchema = fieldSchema['x-component-props'] || {};
|
||||
const { collectionName, currentCollection, correlation } = propsInSchema;
|
||||
const fieldCollection = currentCollection || collectionName || collection.name;
|
||||
|
||||
const collectionField =
|
||||
c.getField(fieldSchema['fieldName']) ||
|
||||
collection.getField(fieldSchema['fieldName']) ||
|
||||
cm.getCollectionField(fieldSchema['x-collection-field']) ||
|
||||
cm.getCollection(fieldCollection + '.' + correlation);
|
||||
const compile = useCompile();
|
||||
|
@ -11,7 +11,14 @@ export function generateNTemplate(key: string) {
|
||||
return `{{t('${key}', { ns: '${NAMESPACE}', nsMode: 'fallback' })}}`;
|
||||
}
|
||||
|
||||
export const tval = (key: string) => nTval(key, { ns: NAMESPACE });
|
||||
export const tval = (key: string, haveNamespace: boolean = true) => {
|
||||
if (haveNamespace) {
|
||||
return nTval(key, { ns: NAMESPACE })
|
||||
} else {
|
||||
return nTval(key)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export function useTranslation() {
|
||||
return useT([NAMESPACE, '@tachybase/plugin-mobile-client', 'client'], {
|
||||
|
@ -29,8 +29,10 @@
|
||||
"Mobile client access address": "Mobile client access address",
|
||||
"The full address is": "The full address is",
|
||||
"Preview": "Preview",
|
||||
"Choices fields":"Choices fields",
|
||||
"Choices fields":"Choices fields",
|
||||
"Text fields":"Text fields",
|
||||
"Display association Textfields": "Display association Textfields",
|
||||
"Display association Choicefields": "Display association Choicefields",
|
||||
"Generic properties":"Generic properties",
|
||||
"Search":"Search",
|
||||
"Please enter search content":"Please enter search content",
|
||||
|
@ -29,8 +29,10 @@
|
||||
"Mobile client access address": "移动端访问地址",
|
||||
"The full address is": "完整的地址是",
|
||||
"Preview": "预览",
|
||||
"Choices fields":"选择字段",
|
||||
"Choices fields":"选择字段",
|
||||
"Text fields":"文本字段",
|
||||
"Display association Textfields": "文本关联字段",
|
||||
"Display association Choicefields": "选择关联字段",
|
||||
"Generic properties":"通用属性",
|
||||
"Search":"搜索",
|
||||
"Please enter search content":"请输入查询内容",
|
||||
|
Loading…
Reference in New Issue
Block a user