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:
bai.zixv 2024-05-09 20:20:38 +08:00 committed by sealday
parent 9e63e1c392
commit 4f26e6e8c9
16 changed files with 584 additions and 179 deletions

View File

@ -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,
};
};

View File

@ -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,
};
};

View File

@ -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 ? (

View File

@ -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;

View File

@ -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,
};
};

View File

@ -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);
};

View File

@ -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,
};
};

View File

@ -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();

View File

@ -1,3 +1,2 @@
export * from './TabSearch';
export * from './TabSearchAction';
export * from './field-item';

View File

@ -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;
}

View File

@ -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) => {

View File

@ -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;
}

View File

@ -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();

View File

@ -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'], {

View File

@ -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",

View File

@ -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":"请输入查询内容",