mirror of
https://gitee.com/nocobase/nocobase.git
synced 2024-11-29 18:58:26 +08:00
refactor: collection fields to initializer items (#4900)
* refactor: add collection-fields-initializer-items * fix: bug * fix: bug * fix: rename and docs * fix: change dir * docs: imporve doc * Update packages/core/client/docs/en-US/core/data-source/collection-fields-to-initializer-items.md Co-authored-by: Zeke Zhang <958414905@qq.com> * Update packages/core/client/docs/en-US/core/data-source/collection-fields-to-initializer-items.md Co-authored-by: Zeke Zhang <958414905@qq.com> --------- Co-authored-by: Zeke Zhang <958414905@qq.com>
This commit is contained in:
parent
ef06db3eb7
commit
1be5b2f578
@ -186,6 +186,10 @@ export default defineConfig({
|
||||
title: 'ExtendCollectionsProvider',
|
||||
link: '/core/data-source/extend-collections-provider',
|
||||
},
|
||||
{
|
||||
title: 'Collection Fields To Initializer Items',
|
||||
link: '/core/data-source/collection-fields-to-initializer-items',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -0,0 +1,339 @@
|
||||
# Collection Fields To Initializer Items
|
||||
|
||||
## 介绍
|
||||
|
||||
![20240718145531](https://static-docs.nocobase.com/20240718145531.png)
|
||||
|
||||
页面上有 `Configure columns` 和 `Configure fields` 两个按钮,鼠标悬浮后显示当前表的字段列表,当点击某个字段后,会插入表格列或者表单项到界面中,这个过程就是从 `Collection Fields` 到 `Initializer Items` 的过程。
|
||||
|
||||
## Configure fields 分类
|
||||
|
||||
`Configure fields` 分为三类:
|
||||
|
||||
- `self collection fields`:当前表的字段
|
||||
- `parent collection fields`:父表的字段
|
||||
- `associated collection fields`:关联表的字段
|
||||
|
||||
![20240718151313](https://static-docs.nocobase.com/20240718151313.png)
|
||||
|
||||
![20240718151040](https://static-docs.nocobase.com/20240718151040.png)
|
||||
|
||||
## CollectionFieldsToInitializerItems
|
||||
|
||||
我们将单个 `Collection Field` 转为 `Initializer Item` 的过程抽象为以下三个步骤:
|
||||
|
||||
- `filter`:过滤字段
|
||||
- `getSchema`:获取字段对应的 schema
|
||||
- `getInitializerItem`:获取字段对应的 initializer item
|
||||
|
||||
> 关于 Schema 请查看 [UI Schema](https://docs.nocobase.com/development/client/ui-schema/what-is-ui-schema)。 <br />
|
||||
> 关于 Initializer item,可以参考 [SchemaInitializer](/core/ui-schema/schema-initializer)。<br />
|
||||
> 两者的关系是 Initializer item 通过类似 `onClick` 的事件触发,将 Schema 插入到 Schema 树中,并渲染到界面上。
|
||||
|
||||
`CollectionFieldsToInitializerItems` 是一个组件,用于将 `Collection Fields` 转换为 `Initializer Items`。
|
||||
|
||||
|
||||
```ts
|
||||
const someInitializer = new SchemaInitializer({
|
||||
// ...
|
||||
items: [
|
||||
{
|
||||
name: 'collectionFields',
|
||||
Component: CollectionFieldsToInitializerItems,
|
||||
},
|
||||
// ...
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Types
|
||||
|
||||
```ts
|
||||
interface CollectionFieldContext {
|
||||
fieldSchema: ISchema;
|
||||
collection?: InheritanceCollectionMixin & Collection;
|
||||
dataSource: DataSource;
|
||||
form: Form<any>;
|
||||
actionContext: ReturnType<typeof useActionContext>;
|
||||
t: TFunction<"translation", undefined>;
|
||||
collectionManager: CollectionManager;
|
||||
dataSourceManager: DataSourceManager;
|
||||
compile: (source: any, ext?: any) => any
|
||||
targetCollection?: Collection;
|
||||
}
|
||||
|
||||
interface CommonCollectionFieldsProps {
|
||||
block: string;
|
||||
isReadPretty?: (context: CollectionFieldContext) => boolean;
|
||||
filter?: (collectionField: CollectionFieldOptions, context: CollectionFieldContext) => boolean;
|
||||
getSchema: (collectionField: CollectionFieldOptions, context: CollectionFieldContext) => CollectionFieldGetSchemaResult;
|
||||
getInitializerItem?: (collectionField: CollectionFieldOptions, context: CollectionFieldContext) => CollectionFieldGetInitializerItemResult;
|
||||
}
|
||||
|
||||
interface SelfCollectionFieldsProps extends CommonCollectionFieldsProps {}
|
||||
interface ParentCollectionFieldsProps extends CommonCollectionFieldsProps {}
|
||||
interface AssociationCollectionFieldsProps extends Omit<CommonCollectionFieldsProps, 'filter'> {
|
||||
filterSelfField?: CommonCollectionFieldsProps['filter'];
|
||||
filterAssociationField?: CommonCollectionFieldsProps['filter'];
|
||||
}
|
||||
|
||||
interface CollectionFieldsProps {
|
||||
/**
|
||||
* Block name.
|
||||
*/
|
||||
block: string;
|
||||
selfField: Omit<SelfCollectionFieldsProps, 'block' | 'context'>;
|
||||
parentField?: Omit<ParentCollectionFieldsProps, 'block' | 'context'>;
|
||||
associationField?: Omit<AssociationCollectionFieldsProps, 'block' | 'context'>;
|
||||
}
|
||||
```
|
||||
|
||||
#### CollectionFieldsProps
|
||||
|
||||
- `block`:区块名称
|
||||
- `selfField`:当前表字段配置
|
||||
- `parentField`:父表字段配置
|
||||
- `associationField`:关联表字段配置
|
||||
|
||||
#### CommonCollectionFieldsProps
|
||||
|
||||
- `block`:区块名称
|
||||
- `isReadPretty`:是否为只读模式
|
||||
- `filter`:过滤字段
|
||||
- `getSchema`:获取字段对应的 schema
|
||||
- `getInitializerItem`:获取字段对应的 initializer item
|
||||
|
||||
##### 公共 Schema
|
||||
|
||||
其中 `getSchema` 内部包含了公共的部分,所以并不要求返回整个 Schema,只需要返回差异部分即可。公共部分如下:
|
||||
|
||||
```ts
|
||||
const defaultSchema: CollectionFieldDefaultSchema = {
|
||||
type: 'string',
|
||||
title: collectionField?.uiSchema?.title || collectionField.name,
|
||||
name: collectionField.name,
|
||||
'x-component': 'CollectionField',
|
||||
'x-collection-field': `${collection.name}.${collectionField.name}`,
|
||||
'x-read-pretty': collectionField?.uiSchema?.['x-read-pretty'],
|
||||
};
|
||||
```
|
||||
|
||||
其中 [CollectionField](/core/data-source/collection-field) 用于动态渲染字段。
|
||||
|
||||
##### 公共 Initializer Item
|
||||
|
||||
同样 `getInitializerItem` 内部包含了公共的部分,所以并不要求返回整个 Initializer Item,只需要返回 `CollectionFieldInitializer`(文档 TODO)组件对应的 `find` 和 `remove`。
|
||||
|
||||
#### AssociationCollectionFieldsProps
|
||||
|
||||
- `filterSelfField`:过滤当前表字段
|
||||
- `filterAssociationField`:过滤关联表字段
|
||||
|
||||
其他属性同 `CommonCollectionFieldsProps`。
|
||||
|
||||
#### CollectionFieldContext
|
||||
|
||||
- [fieldSchema](/core/ui-schema/designable#usefieldschema):当前 schema
|
||||
- [collection](/core/data-source/collection):当前表
|
||||
- [dataSource](/core/data-source/data-source-provider#usedatasource):数据源
|
||||
- `form`:表单
|
||||
- [actionContext](/components/action#actioncontext):操作上下文
|
||||
- `t`:国际化
|
||||
- [collectionManager](/core/data-source/collection-manager-provider#usecollectionmanager):表管理器
|
||||
- [dataSourceManager](/core/data-source/data-source-manager-provider#usedatasourcemanager):数据源管理器
|
||||
- `compile`:编译函数
|
||||
- `targetCollection`:如果是关联表字段,表示关联表
|
||||
|
||||
### Example
|
||||
|
||||
我们以 `Collection Field` 转为 `FormItem` 为例:
|
||||
|
||||
#### 定义
|
||||
|
||||
```tsx | pure
|
||||
|
||||
export const CollectionFieldsToFormInitializerItems: FC<{ block?: string }> = (props) => {
|
||||
const block = props?.block || 'Form';
|
||||
const fieldItemSchema = {
|
||||
'x-toolbar': 'FormItemSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:FormItem',
|
||||
'x-decorator': 'FormItem',
|
||||
};
|
||||
|
||||
const initializerItem = {
|
||||
remove: removeGridFormItem,
|
||||
}
|
||||
return <CollectionFieldsToInitializerItems
|
||||
block={block}
|
||||
selfField={{
|
||||
filter: (field) => !field.treeChildren,
|
||||
getSchema: (field, { targetCollection }) => {
|
||||
const isFileCollection = targetCollection?.template === 'file';
|
||||
const isAssociationField = targetCollection;
|
||||
const fieldNames = field?.uiSchema?.['x-component-props']?.['fieldNames'];
|
||||
|
||||
return {
|
||||
...fieldItemSchema,
|
||||
'x-component-props': isFileCollection
|
||||
? { fieldNames: { label: 'preview', value: 'id' } }
|
||||
: isAssociationField && fieldNames? { fieldNames: { ...fieldNames, label: targetCollection?.titleField || fieldNames.label }}
|
||||
: {},
|
||||
}
|
||||
},
|
||||
getInitializerItem: () => {
|
||||
return {
|
||||
...initializerItem,
|
||||
find: props?.block === 'Kanban' ? findKanbanFormItem : undefined
|
||||
}
|
||||
}
|
||||
}}
|
||||
parentField={{
|
||||
getSchema: () => fieldItemSchema,
|
||||
getInitializerItem: () => initializerItem,
|
||||
}}
|
||||
associationField={{
|
||||
filterSelfField: (field) => {
|
||||
if (block !== 'Form') return true;
|
||||
return field?.interface === 'm2o'
|
||||
},
|
||||
filterAssociationField(collectionField) {
|
||||
return collectionField?.interface && !['subTable'].includes(collectionField?.interface) && !collectionField.treeChildren
|
||||
},
|
||||
getSchema: () => fieldItemSchema,
|
||||
getInitializerItem: () => initializerItem,
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
##### selfField
|
||||
|
||||
- `filter`:过滤字段
|
||||
|
||||
`filter: (field) => !field.treeChildren` 表示过滤掉树形结构的字段。
|
||||
|
||||
因为 ?
|
||||
|
||||
- `getSchema`:获取字段对应的 schema
|
||||
|
||||
参考 [FormItem](/components/form-item) 以及 [Field](/components/checkbox) 文档,希望最终获得的 Schema 如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "string",
|
||||
"name": "nickname",
|
||||
"x-toolbar": "FormItemSchemaToolbar",
|
||||
"x-settings": "fieldSettings:FormItem",
|
||||
"x-component": "CollectionField",
|
||||
"x-decorator": "FormItem",
|
||||
"x-collection-field": "users.nickname",
|
||||
"x-component-props": {
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
其中 [公共部分](/core/data-source/collection-fields-initializer-items#commoncollectionfieldsprops) 如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "string",
|
||||
"name": "nickname",
|
||||
"x-component": "CollectionField",
|
||||
"x-collection-field": "users.nickname",
|
||||
"x-read-pretty": true
|
||||
}
|
||||
```
|
||||
|
||||
所以我们只需要返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"x-toolbar": "FormItemSchemaToolbar",
|
||||
"x-settings": "fieldSettings:FormItem",
|
||||
"x-decorator": "FormItem",
|
||||
"x-component-props": {
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `getInitializerItem`:获取字段对应的 initializer item
|
||||
|
||||
因为其对应的 [SchemaInitializer](/core/ui-schema/schema-initializer) 有 wrap 属性,将每个字段包裹在 `Grid` 中,方便布局和拖拽。我们在删除时则不仅需要删除自身的 Schema 还需要删除对应的 `Grid`。所以我们返回:
|
||||
|
||||
```ts
|
||||
{
|
||||
"remove": removeGridFormItem
|
||||
}
|
||||
```
|
||||
|
||||
##### parentField
|
||||
|
||||
略。
|
||||
|
||||
##### associationField
|
||||
|
||||
- `filterSelfField`:过滤当前表字段
|
||||
|
||||
表单这里仅需要展示多对一的关联字段,所以我们过滤掉非多对一关联字段。?
|
||||
|
||||
- `filterAssociationField`:过滤关联表字段
|
||||
|
||||
同样过滤掉树形结构的字段。
|
||||
|
||||
|
||||
#### 使用
|
||||
|
||||
```diff
|
||||
const formItemInitializers = new CompatibleSchemaInitializer({
|
||||
name: 'form:configureFields',
|
||||
wrap: gridRowColWrap,
|
||||
icon: 'SettingOutlined',
|
||||
title: '{{t("Configure fields")}}',
|
||||
items: [
|
||||
+ {
|
||||
+ name: 'collectionFields',
|
||||
+ Component: CollectionFieldsToFormInitializerItems,
|
||||
+ },
|
||||
// ...
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
## CollectionFieldsToFormInitializerItems
|
||||
|
||||
`CollectionFieldsToFormInitializerItems` 是 `CollectionFieldsToInitializerItems` 的一个封装,用于表单场景。
|
||||
|
||||
目前使用在了 `Form`、`List`、`Kanban`、`Grid Card` 和 `Details` 区块中。
|
||||
|
||||
```ts
|
||||
const someInitializer = new SchemaInitializer({
|
||||
// ...
|
||||
items: [
|
||||
{
|
||||
name: 'collectionFields',
|
||||
Component: CollectionFieldsToFormInitializerItems,
|
||||
},
|
||||
// ...
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
## CollectionFieldsToTableInitializerItems
|
||||
|
||||
`CollectionFieldsToTableInitializerItems` 是 `CollectionFieldsToInitializerItems` 的一个封装,用于表格场景。
|
||||
|
||||
目前使用在了 `Table` 和 `Gantt` 区块中。
|
||||
|
||||
```ts
|
||||
const someInitializer = new SchemaInitializer({
|
||||
// ...
|
||||
items: [
|
||||
{
|
||||
name: 'collectionFields',
|
||||
Component: CollectionFieldsToTableInitializerItems,
|
||||
},
|
||||
// ...
|
||||
]
|
||||
})
|
||||
```
|
@ -0,0 +1,309 @@
|
||||
# Collection Fields To Initializer Items
|
||||
|
||||
## 介绍
|
||||
|
||||
![20240718145531](https://static-docs.nocobase.com/20240718145531.png)
|
||||
|
||||
页面上有 `Configure columns` 和 `Configure fields` 两个按钮,点击后显示当前表的字段列表,当点击某个字段后,会插入表单项或者表格列到界面中,这个过程就是从 `Collection Fields` 到 `Initializer Items` 的过程。
|
||||
|
||||
## Configure fields 分类
|
||||
|
||||
`Configure fields` 分为三类:
|
||||
|
||||
- `self collection fields`:当前表的字段
|
||||
- `parent collection fields`:父表的字段
|
||||
- `associated collection fields`:关联表的字段
|
||||
|
||||
![20240718151313](https://static-docs.nocobase.com/20240718151313.png)
|
||||
|
||||
![20240718151040](https://static-docs.nocobase.com/20240718151040.png)
|
||||
|
||||
## CollectionFieldsToInitializerItems
|
||||
|
||||
我们将单个 `Collection Field` 转为 `Initializer Item` 的过程抽象以下三个步骤:
|
||||
|
||||
- `filter`:过滤字段
|
||||
- `getSchema`:获取字段对应的 schema
|
||||
- `getInitializerItem`:获取字段对应的 initializer item
|
||||
|
||||
> 关于 Schema 请查看 [UI Schema](https://docs.nocobase.com/development/client/ui-schema/what-is-ui-schema)。 <br />
|
||||
> 关于 Initializer item,可以参考 [SchemaInitializer](/core/ui-schema/schema-initializer)。<br />
|
||||
> 两者的关系是 Initializer item 通过类似 `onClick` 的事件触发,将 Schema 插入到 Schema 树中,并渲染到界面上。
|
||||
|
||||
`CollectionFieldsToInitializerItems` 是一个组件,用于将 `Collection Fields` 转换为 `Initializer Items`。
|
||||
|
||||
### Types
|
||||
|
||||
```ts
|
||||
interface CollectionFieldContext {
|
||||
fieldSchema: ISchema;
|
||||
collection?: InheritanceCollectionMixin & Collection;
|
||||
dataSource: DataSource;
|
||||
form: Form<any>;
|
||||
actionContext: ReturnType<typeof useActionContext>;
|
||||
t: TFunction<"translation", undefined>;
|
||||
collectionManager: CollectionManager;
|
||||
dataSourceManager: DataSourceManager;
|
||||
compile: (source: any, ext?: any) => any
|
||||
targetCollection?: Collection;
|
||||
}
|
||||
|
||||
interface CommonCollectionFieldsProps {
|
||||
block: string;
|
||||
isReadPretty?: (context: CollectionFieldContext) => boolean;
|
||||
filter?: (collectionField: CollectionFieldOptions, context: CollectionFieldContext) => boolean;
|
||||
getSchema: (collectionField: CollectionFieldOptions, context: CollectionFieldContext & {
|
||||
defaultSchema: CollectionFieldDefaultSchema
|
||||
targetCollection?: Collection
|
||||
collectionFieldInterface?: CollectionFieldInterface
|
||||
}) => CollectionFieldGetSchemaResult;
|
||||
getInitializerItem?: (collectionField: CollectionFieldOptions, context: CollectionFieldContext & {
|
||||
schema: ISchema;
|
||||
defaultInitializerItem: CollectionFieldDefaultInitializerItem;
|
||||
targetCollection?: Collection
|
||||
collectionFieldInterface?: CollectionFieldInterface
|
||||
}) => CollectionFieldGetInitializerItemResult;
|
||||
}
|
||||
|
||||
interface SelfCollectionFieldsProps extends CommonCollectionFieldsProps {}
|
||||
interface ParentCollectionFieldsProps extends CommonCollectionFieldsProps {}
|
||||
interface AssociationCollectionFieldsProps extends Omit<CommonCollectionFieldsProps, 'filter'> {
|
||||
filterSelfField?: CommonCollectionFieldsProps['filter'];
|
||||
filterAssociationField?: CommonCollectionFieldsProps['filter'];
|
||||
}
|
||||
|
||||
interface CollectionFieldsProps {
|
||||
/**
|
||||
* Block name.
|
||||
*/
|
||||
block: string;
|
||||
selfField: Omit<SelfCollectionFieldsProps, 'block' | 'context'>;
|
||||
parentField?: Omit<ParentCollectionFieldsProps, 'block' | 'context'>;
|
||||
associationField?: Omit<AssociationCollectionFieldsProps, 'block' | 'context'>;
|
||||
}
|
||||
```
|
||||
|
||||
#### CollectionFieldsProps
|
||||
|
||||
- `block`:区块名称
|
||||
- `selfField`:当前表字段配置
|
||||
- `parentField`:父表字段配置
|
||||
- `associationField`:关联表字段配置
|
||||
|
||||
#### CommonCollectionFieldsProps
|
||||
|
||||
- `block`:区块名称
|
||||
- `isReadPretty`:是否为只读模式
|
||||
- `filter`:过滤字段
|
||||
- `getSchema`:获取字段对应的 schema
|
||||
- `getInitializerItem`:获取字段对应的 initializer item
|
||||
|
||||
##### 公共 Schema
|
||||
|
||||
其中 `getSchema` 内部包含了公共的部分,所以并不要求返回整个 Schema,只需要返回差异部分即可。公共部分如下:
|
||||
|
||||
```ts
|
||||
const defaultSchema: CollectionFieldDefaultSchema = {
|
||||
type: 'string',
|
||||
title: collectionField?.uiSchema?.title || collectionField.name,
|
||||
name: collectionField.name,
|
||||
'x-component': 'CollectionField',
|
||||
'x-collection-field': `${collection.name}.${collectionField.name}`,
|
||||
'x-read-pretty': collectionField?.uiSchema?.['x-read-pretty'],
|
||||
};
|
||||
```
|
||||
|
||||
其中 [CollectionField](/core/data-source/collection-field) 用于动态渲染字段。
|
||||
|
||||
##### 公共 Initializer Item
|
||||
|
||||
同样 `getInitializerItem` 内部包含了公共的部分,所以并不要求返回整个 Initializer Item,只需要返回 `CollectionFieldInitializer`(文档 TODO)组件对应的 `find` 和 `remove`。
|
||||
|
||||
#### AssociationCollectionFieldsProps
|
||||
|
||||
- `filterSelfField`:过滤当前表字段
|
||||
- `filterAssociationField`:过滤关联表字段
|
||||
|
||||
其他属性同 `CommonCollectionFieldsProps`。
|
||||
|
||||
#### CollectionFieldContext
|
||||
|
||||
- [fieldSchema](/core/ui-schema/designable#usefieldschema):当前 schema
|
||||
- [collection](/core/data-source/collection):当前表
|
||||
- [dataSource](/core/data-source/data-source-provider#usedatasource):数据源
|
||||
- `form`:表单
|
||||
- [actionContext](/components/action#actioncontext):操作上下文
|
||||
- `t`:国际化
|
||||
- [collectionManager](/core/data-source/collection-manager-provider#usecollectionmanager):表管理器
|
||||
- [dataSourceManager](/core/data-source/data-source-manager-provider#usedatasourcemanager):数据源管理器
|
||||
- `compile`:编译函数
|
||||
- `targetCollection`:如果是关联表字段,表示关联表
|
||||
|
||||
### Example
|
||||
|
||||
我们以 `Collection Field` 转为 `FormItem` 为例:
|
||||
|
||||
#### 定义
|
||||
|
||||
```tsx | pure
|
||||
|
||||
export const CollectionFieldsToFormInitializerItems: FC<{ block?: string }> = (props) => {
|
||||
const block = props?.block || 'Form';
|
||||
const fieldItemSchema = {
|
||||
'x-toolbar': 'FormItemSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:FormItem',
|
||||
'x-decorator': 'FormItem',
|
||||
};
|
||||
|
||||
const initializerItem = {
|
||||
remove: removeGridFormItem,
|
||||
}
|
||||
return <CollectionFieldsToInitializerItems
|
||||
block={block}
|
||||
selfField={{
|
||||
filter: (field) => !field.treeChildren,
|
||||
getSchema: (field, { targetCollection }) => {
|
||||
const isFileCollection = targetCollection?.template === 'file';
|
||||
const isAssociationField = targetCollection;
|
||||
const fieldNames = field?.uiSchema?.['x-component-props']?.['fieldNames'];
|
||||
|
||||
return {
|
||||
...fieldItemSchema,
|
||||
'x-component-props': isFileCollection
|
||||
? { fieldNames: { label: 'preview', value: 'id' } }
|
||||
: isAssociationField && fieldNames? { fieldNames: { ...fieldNames, label: targetCollection?.titleField || fieldNames.label }}
|
||||
: {},
|
||||
}
|
||||
},
|
||||
getInitializerItem: () => {
|
||||
return {
|
||||
...initializerItem,
|
||||
find: props?.block === 'Kanban' ? findKanbanFormItem : undefined
|
||||
}
|
||||
}
|
||||
}}
|
||||
parentField={{
|
||||
getSchema: () => fieldItemSchema,
|
||||
getInitializerItem: () => initializerItem,
|
||||
}}
|
||||
associationField={{
|
||||
filterSelfField: (field) => {
|
||||
if (block !== 'Form') return true;
|
||||
return field?.interface === 'm2o'
|
||||
},
|
||||
filterAssociationField(collectionField) {
|
||||
return collectionField?.interface && !['subTable'].includes(collectionField?.interface) && !collectionField.treeChildren
|
||||
},
|
||||
getSchema: () => fieldItemSchema,
|
||||
getInitializerItem: () => initializerItem,
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
##### selfField
|
||||
|
||||
- `filter`:过滤字段
|
||||
|
||||
`filter: (field) => !field.treeChildren` 表示过滤掉树形结构的字段。
|
||||
|
||||
因为 ?
|
||||
|
||||
- `getSchema`:获取字段对应的 schema
|
||||
|
||||
参考 [FormItem](/components/form-item) 以及 [Field](/components/checkbox) 文档,希望最终获得的 Schema 如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "string",
|
||||
"name": "nickname",
|
||||
"x-toolbar": "FormItemSchemaToolbar",
|
||||
"x-settings": "fieldSettings:FormItem",
|
||||
"x-component": "CollectionField",
|
||||
"x-decorator": "FormItem",
|
||||
"x-collection-field": "users.nickname",
|
||||
"x-component-props": {
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
其中 [公共部分](/core/data-source/collection-fields-initializer-items#commoncollectionfieldsprops) 如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "string",
|
||||
"name": "nickname",
|
||||
"x-component": "CollectionField",
|
||||
"x-collection-field": "users.nickname",
|
||||
"x-read-pretty": true
|
||||
}
|
||||
```
|
||||
|
||||
所以我们只需要返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"x-toolbar": "FormItemSchemaToolbar",
|
||||
"x-settings": "fieldSettings:FormItem",
|
||||
"x-decorator": "FormItem",
|
||||
"x-component-props": {
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `getInitializerItem`:获取字段对应的 initializer item
|
||||
|
||||
因为其对应的 [SchemaInitializer](/core/ui-schema/schema-initializer) 有 wrap 属性,将每个字段包裹在 `Grid` 中,方便布局和拖拽。我们在删除时则不仅需要删除自身的 Schema 还需要删除对应的 `Grid`。所以我们返回:
|
||||
|
||||
```ts
|
||||
{
|
||||
"remove": removeGridFormItem
|
||||
}
|
||||
```
|
||||
|
||||
##### parentField
|
||||
|
||||
略。
|
||||
|
||||
##### associationField
|
||||
|
||||
- `filterSelfField`:过滤当前表字段
|
||||
|
||||
表单这里仅需要展示多对一的关联字段,所以我们过滤掉非多对一关联字段。?
|
||||
|
||||
- `filterAssociationField`:过滤关联表字段
|
||||
|
||||
同样过滤掉树形结构的字段。
|
||||
|
||||
|
||||
#### 使用
|
||||
|
||||
```diff
|
||||
const formItemInitializers = new CompatibleSchemaInitializer({
|
||||
name: 'form:configureFields',
|
||||
wrap: gridRowColWrap,
|
||||
icon: 'SettingOutlined',
|
||||
title: '{{t("Configure fields")}}',
|
||||
items: [
|
||||
+ {
|
||||
+ name: 'collectionFields',
|
||||
+ Component: CollectionFieldsToFormInitializerItems,
|
||||
+ },
|
||||
// ...
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
## CollectionFieldsToFormInitializerItems
|
||||
|
||||
`CollectionFieldsToFormInitializerItems` 是 `CollectionFieldsToInitializerItems` 的一个封装,用于表单场景。
|
||||
|
||||
目前使用在了 `Form`、`List`、`Kanban`、`Grid Card` 和 `Details` 区块中。
|
||||
|
||||
## CollectionFieldsToTableInitializerItems
|
||||
|
||||
`CollectionFieldsToTableInitializerItems` 是 `CollectionFieldsToInitializerItems` 的一个封装,用于表格场景。
|
||||
|
||||
目前使用在了 `Table` 和 `Gantt` 区块中。
|
||||
|
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { Schema } from '@formily/json-schema';
|
||||
import { CollectionFieldsToInitializerItems } from './CollectionFieldsToInitializerItems';
|
||||
import { removeGridFormItem, findSchema } from '../../schema-initializer/utils';
|
||||
|
||||
export const findKanbanFormItem = (schema: Schema, key: string, action: string) => {
|
||||
const s = findSchema(schema, 'x-component', 'Kanban');
|
||||
return findSchema(s, key, action);
|
||||
};
|
||||
|
||||
export const CollectionFieldsToFormInitializerItems: FC<{ block?: string }> = (props) => {
|
||||
const block = props?.block || 'Form';
|
||||
const fieldItemSchema = {
|
||||
'x-toolbar': 'FormItemSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:FormItem',
|
||||
'x-decorator': 'FormItem',
|
||||
};
|
||||
|
||||
const initializerItem = {
|
||||
remove: removeGridFormItem,
|
||||
};
|
||||
return (
|
||||
<CollectionFieldsToInitializerItems
|
||||
block={block}
|
||||
selfField={{
|
||||
filter: (field) => !field.treeChildren,
|
||||
getSchema: (field, { targetCollection }) => {
|
||||
const isFileCollection = targetCollection?.template === 'file';
|
||||
const isAssociationField = targetCollection;
|
||||
const fieldNames = field?.uiSchema?.['x-component-props']?.['fieldNames'];
|
||||
|
||||
return {
|
||||
...fieldItemSchema,
|
||||
'x-component-props': isFileCollection
|
||||
? { fieldNames: { label: 'preview', value: 'id' } }
|
||||
: isAssociationField && fieldNames
|
||||
? { fieldNames: { ...fieldNames, label: targetCollection?.titleField || fieldNames.label } }
|
||||
: {},
|
||||
};
|
||||
},
|
||||
getInitializerItem: () => {
|
||||
return {
|
||||
...initializerItem,
|
||||
find: props?.block === 'Kanban' ? findKanbanFormItem : undefined,
|
||||
};
|
||||
},
|
||||
}}
|
||||
parentField={{
|
||||
getSchema: () => fieldItemSchema,
|
||||
getInitializerItem: () => initializerItem,
|
||||
}}
|
||||
associationField={{
|
||||
filterSelfField: (field) => {
|
||||
if (block !== 'Form') return true;
|
||||
return field?.interface === 'm2o';
|
||||
},
|
||||
filterAssociationField(collectionField) {
|
||||
return (
|
||||
collectionField?.interface &&
|
||||
!['subTable'].includes(collectionField?.interface) &&
|
||||
!collectionField.treeChildren
|
||||
);
|
||||
},
|
||||
getSchema: () => fieldItemSchema,
|
||||
getInitializerItem: () => initializerItem,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { CollectionFieldsProps, useCollectionFieldContext } from './utils';
|
||||
import { AssociationCollectionFields, ParentCollectionFields, SelfFields } from './items';
|
||||
|
||||
export const CollectionFieldsToInitializerItems: FC<CollectionFieldsProps> = (props) => {
|
||||
const { selfField, parentField, associationField, block } = props;
|
||||
const context = useCollectionFieldContext();
|
||||
if (!context.collection) return null;
|
||||
return (
|
||||
<>
|
||||
<SelfFields block={block} {...selfField} context={{ ...context, collection: context.collection }} />
|
||||
{parentField && (
|
||||
<ParentCollectionFields
|
||||
block={block}
|
||||
{...parentField}
|
||||
context={{ ...context, collection: context.collection }}
|
||||
/>
|
||||
)}
|
||||
{associationField && (
|
||||
<AssociationCollectionFields
|
||||
block={block}
|
||||
{...associationField}
|
||||
context={{ ...context, collection: context.collection }}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,127 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { CollectionFieldsToInitializerItems } from './CollectionFieldsToInitializerItems';
|
||||
import { findTableColumn, removeGridFormItem, removeTableColumn } from '../../schema-initializer/utils';
|
||||
|
||||
const quickEditField = [
|
||||
'attachment',
|
||||
'textarea',
|
||||
'markdown',
|
||||
'json',
|
||||
'richText',
|
||||
'polygon',
|
||||
'circle',
|
||||
'point',
|
||||
'lineString',
|
||||
];
|
||||
|
||||
export const CollectionFieldsToTableInitializerItems: FC = (props) => {
|
||||
function isReadPretty({ fieldSchema, form }) {
|
||||
const isSubTable = fieldSchema['x-component'] === 'AssociationField.SubTable';
|
||||
const isReadPretty = isSubTable ? form.readPretty : true;
|
||||
|
||||
return isReadPretty;
|
||||
}
|
||||
return (
|
||||
<CollectionFieldsToInitializerItems
|
||||
block={'Table'}
|
||||
selfField={{
|
||||
isReadPretty,
|
||||
filter: (field) => field.interface !== 'subTable' && !field.treeChildren,
|
||||
getSchema: (field, { targetCollection, fieldSchema, form }) => {
|
||||
const isFileCollection = targetCollection?.template === 'file';
|
||||
const isPreviewComponent = field.uiSchema?.['x-component'] === 'Preview';
|
||||
const isSubTable = fieldSchema['x-component'] === 'AssociationField.SubTable';
|
||||
const readPretty = isReadPretty({ fieldSchema, form });
|
||||
|
||||
return {
|
||||
'x-component-props': isFileCollection
|
||||
? {
|
||||
fieldNames: {
|
||||
label: 'preview',
|
||||
value: 'id',
|
||||
},
|
||||
}
|
||||
: isPreviewComponent
|
||||
? { size: 'small' }
|
||||
: {},
|
||||
'x-read-pretty': readPretty || field.uiSchema?.['x-read-pretty'],
|
||||
'x-decorator': isSubTable
|
||||
? quickEditField.includes(field.interface) || isFileCollection
|
||||
? 'QuickEdit'
|
||||
: 'FormItem'
|
||||
: null,
|
||||
'x-decorator-props': {
|
||||
labelStyle: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
getInitializerItem: () => {
|
||||
return {
|
||||
find: findTableColumn,
|
||||
remove: removeTableColumn,
|
||||
};
|
||||
},
|
||||
}}
|
||||
parentField={{
|
||||
isReadPretty,
|
||||
getSchema(field, { targetCollection, fieldSchema, form }) {
|
||||
const isFileCollection = targetCollection?.template === 'file';
|
||||
const isSubTable = fieldSchema['x-component'] === 'AssociationField.SubTable';
|
||||
const readPretty = isReadPretty({ fieldSchema, form });
|
||||
|
||||
return {
|
||||
'x-component-props': isFileCollection
|
||||
? {
|
||||
fieldNames: {
|
||||
label: 'preview',
|
||||
value: 'id',
|
||||
},
|
||||
}
|
||||
: {},
|
||||
'x-read-pretty': readPretty || field.uiSchema?.['x-read-pretty'],
|
||||
'x-decorator': isSubTable
|
||||
? quickEditField.includes(field.interface) || isFileCollection
|
||||
? 'QuickEdit'
|
||||
: 'FormItem'
|
||||
: null,
|
||||
'x-decorator-props': {
|
||||
labelStyle: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
getInitializerItem() {
|
||||
return {
|
||||
remove: removeGridFormItem,
|
||||
};
|
||||
},
|
||||
}}
|
||||
associationField={{
|
||||
filterAssociationField(collectionField) {
|
||||
return !['subTable'].includes(collectionField.interface) && !collectionField.treeChildren;
|
||||
},
|
||||
getSchema() {
|
||||
return {};
|
||||
},
|
||||
getInitializerItem() {
|
||||
return {
|
||||
find: findTableColumn,
|
||||
remove: removeTableColumn,
|
||||
};
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
export * from './CollectionFieldsToInitializerItems';
|
||||
export * from './CollectionFieldsToFormInitializerItems';
|
||||
export * from './CollectionFieldsToTableInitializerItems';
|
@ -0,0 +1,73 @@
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { InheritanceCollectionMixin } from '../../../collection-manager';
|
||||
import { AssociationCollectionFieldsProps, getInitializerItemsByFields } from '../utils';
|
||||
import {
|
||||
SchemaInitializerChildren,
|
||||
SchemaInitializerItemGroup,
|
||||
SchemaInitializerItemType,
|
||||
} from '../../../application/schema-initializer';
|
||||
|
||||
export const AssociationCollectionFields: FC<AssociationCollectionFieldsProps> = (props) => {
|
||||
const { filterAssociationField, filterSelfField = () => true, getSchema, ...otherProps } = props;
|
||||
const { collection, t, collectionManager } = props.context;
|
||||
const fields = collection.getFields();
|
||||
const associationInterfaces = ['o2o', 'oho', 'obo', 'm2o']; // 关联字段类型
|
||||
const associationFields = fields
|
||||
.filter((field) => {
|
||||
return associationInterfaces.includes(field.interface);
|
||||
})
|
||||
.filter((field) => filterSelfField(field, props.context));
|
||||
|
||||
if (!associationFields.length) return null;
|
||||
|
||||
const children = associationFields
|
||||
.map((associationField) => {
|
||||
// 获取关联表
|
||||
const associationCollection = collectionManager.getCollection<InheritanceCollectionMixin>(
|
||||
associationField.target!,
|
||||
)!;
|
||||
if (!associationCollection) return null;
|
||||
// 获取父表
|
||||
const associationCollectionFields = associationCollection?.getAllFields();
|
||||
if (!associationCollectionFields.length) return null;
|
||||
return { associationField, associationCollection, associationCollectionFields };
|
||||
})
|
||||
.filter(Boolean)
|
||||
// 修改数据结构
|
||||
.map(({ associationField, associationCollection, associationCollectionFields }: any) => {
|
||||
const newContext = {
|
||||
...props.context,
|
||||
collection: associationCollection,
|
||||
};
|
||||
|
||||
const getAssociationFieldSchema: AssociationCollectionFieldsProps['getSchema'] = (field, context) => {
|
||||
const schema = getSchema(field, context);
|
||||
return {
|
||||
...(schema || {}),
|
||||
'x-read-pretty': true,
|
||||
name: `${associationField.name}.${field.name}`,
|
||||
'x-collection-field': `${collection.name}.${associationField.name}.${field.name}`,
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'subMenu',
|
||||
name: associationField.uiSchema?.title,
|
||||
title: associationField.uiSchema?.title,
|
||||
children: getInitializerItemsByFields(
|
||||
{
|
||||
...otherProps,
|
||||
filter: filterAssociationField,
|
||||
getSchema: getAssociationFieldSchema,
|
||||
},
|
||||
associationCollectionFields!,
|
||||
newContext,
|
||||
),
|
||||
} as SchemaInitializerItemType;
|
||||
});
|
||||
|
||||
if (!children.length) return null;
|
||||
|
||||
return <SchemaInitializerItemGroup title={t('Display association fields')}>{children}</SchemaInitializerItemGroup>;
|
||||
};
|
@ -0,0 +1,47 @@
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { CollectionFieldOptions } from '../../collection/Collection';
|
||||
import { InheritanceCollectionMixin } from '../../../collection-manager';
|
||||
import { ParentCollectionFieldsProps, getInitializerItemsByFields, useCollectionFieldContext } from '../utils';
|
||||
import { SchemaInitializerChildren, SchemaInitializerItemType } from '../../../application/schema-initializer';
|
||||
|
||||
export const ParentCollectionFields: FC<ParentCollectionFieldsProps> = (props) => {
|
||||
const context = useCollectionFieldContext();
|
||||
const { collection, t, collectionManager } = context;
|
||||
|
||||
const parentCollectionNames = collection.getParentCollectionsName();
|
||||
if (!parentCollectionNames.length) return null;
|
||||
|
||||
const children = parentCollectionNames
|
||||
.map((parentCollectionName) => {
|
||||
// 获取父表的字段
|
||||
const parentCollectionFields = collection.getParentCollectionFields(parentCollectionName);
|
||||
// 如果没有父表字段,返回 null
|
||||
if (parentCollectionFields.length === 0) return null;
|
||||
// 获取父表
|
||||
const parentCollection = collectionManager.getCollection<InheritanceCollectionMixin>(parentCollectionName)!;
|
||||
return { parentCollection, parentCollectionFields };
|
||||
})
|
||||
// 过滤掉 null
|
||||
.filter(Boolean)
|
||||
// 修改数据结构
|
||||
.map((options) => {
|
||||
const { parentCollection, parentCollectionFields } = options as {
|
||||
parentCollection: InheritanceCollectionMixin;
|
||||
parentCollectionFields: CollectionFieldOptions[];
|
||||
};
|
||||
const newContext = {
|
||||
...context,
|
||||
collection: parentCollection,
|
||||
};
|
||||
|
||||
return {
|
||||
type: 'itemGroup',
|
||||
divider: true,
|
||||
title: t(`Parent collection fields`) + '(' + context.compile(parentCollection.title) + ')',
|
||||
children: getInitializerItemsByFields(props, parentCollectionFields, newContext),
|
||||
} as SchemaInitializerItemType;
|
||||
});
|
||||
|
||||
return <SchemaInitializerChildren>{children}</SchemaInitializerChildren>;
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { SelfCollectionFieldsProps, getInitializerItemsByFields, useCollectionFieldContext } from '../utils';
|
||||
import { SchemaInitializerItemGroup } from '../../../application/schema-initializer';
|
||||
|
||||
export const SelfFields: FC<SelfCollectionFieldsProps> = (props) => {
|
||||
const callbackContext = useCollectionFieldContext();
|
||||
const { t, collection } = callbackContext;
|
||||
const fields = collection.getFields();
|
||||
const children = getInitializerItemsByFields(props, fields, callbackContext);
|
||||
|
||||
return <SchemaInitializerItemGroup title={t('Display fields')}>{children}</SchemaInitializerItemGroup>;
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
export { SelfFields } from './SelfFields';
|
||||
export { ParentCollectionFields } from './ParentCollectionFields';
|
||||
export { AssociationCollectionFields } from './AssociationCollectionFields';
|
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import {
|
||||
CollectionFieldDefaultSchema,
|
||||
CollectionFieldDefaultInitializerItem,
|
||||
CommonCollectionFieldsProps,
|
||||
} from './type';
|
||||
import { CollectionFieldOptions } from '../../collection/Collection';
|
||||
import { CollectionFieldContext } from './useCollectionFieldContext';
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
import { SchemaInitializerItemType } from '../../../application/schema-initializer';
|
||||
|
||||
export function getInitializerItemsByFields(
|
||||
props: CommonCollectionFieldsProps,
|
||||
fields: CollectionFieldOptions[],
|
||||
context: CollectionFieldContext,
|
||||
) {
|
||||
const {
|
||||
block,
|
||||
isReadPretty = ({ form }) => form.readPretty,
|
||||
filter = () => true,
|
||||
getInitializerItem = () => ({}),
|
||||
getSchema = () => ({}),
|
||||
} = props;
|
||||
|
||||
const { collectionManager, collection, dataSourceManager, actionContext } = context;
|
||||
const action = actionContext.fieldSchema?.['x-action'];
|
||||
if (!collection) return [];
|
||||
return fields
|
||||
.map((collectionField) => {
|
||||
const targetCollection = collectionManager.getCollection(collectionField.target!);
|
||||
const collectionFieldInterface = dataSourceManager.collectionFieldInterfaceManager.getFieldInterface(
|
||||
collectionField.interface,
|
||||
);
|
||||
return {
|
||||
collectionField,
|
||||
context: {
|
||||
...context,
|
||||
targetCollection,
|
||||
collectionFieldInterface,
|
||||
},
|
||||
};
|
||||
})
|
||||
.filter(({ collectionField }) => collectionField.interface)
|
||||
.filter(({ collectionField, context }) => {
|
||||
return filter(collectionField, context);
|
||||
})
|
||||
.map(({ collectionField, context }) => {
|
||||
const defaultSchema: CollectionFieldDefaultSchema = {
|
||||
type: 'string',
|
||||
title: collectionField?.uiSchema?.title || collectionField.name,
|
||||
name: collectionField.name,
|
||||
'x-component': 'CollectionField',
|
||||
'x-collection-field': `${collection.name}.${collectionField.name}`,
|
||||
'x-read-pretty': collectionField?.uiSchema?.['x-read-pretty'],
|
||||
};
|
||||
const customSchema = getSchema(collectionField, { ...context, defaultSchema: defaultSchema });
|
||||
const schema = {
|
||||
...defaultSchema,
|
||||
...(customSchema || {}),
|
||||
};
|
||||
return {
|
||||
collectionField,
|
||||
schema,
|
||||
context: {
|
||||
...context,
|
||||
schema,
|
||||
},
|
||||
};
|
||||
})
|
||||
.map(({ collectionField, context }) => {
|
||||
const defaultInitializerItem = {
|
||||
type: 'item',
|
||||
name: collectionField.name,
|
||||
title: collectionField?.uiSchema?.title || collectionField.name,
|
||||
Component: 'CollectionFieldInitializer',
|
||||
schemaInitialize: (s: ISchema) => {
|
||||
context.collectionFieldInterface?.schemaInitialize?.(s, {
|
||||
field: collectionField,
|
||||
block,
|
||||
readPretty: isReadPretty?.(context),
|
||||
action,
|
||||
targetCollection: context.targetCollection,
|
||||
});
|
||||
},
|
||||
schema: context.schema,
|
||||
} as CollectionFieldDefaultInitializerItem;
|
||||
return {
|
||||
collectionField,
|
||||
context,
|
||||
defaultInitializerItem,
|
||||
};
|
||||
})
|
||||
.map(({ collectionField, defaultInitializerItem, context }) => {
|
||||
const customInitializerItem = getInitializerItem(collectionField, {
|
||||
...context,
|
||||
defaultInitializerItem,
|
||||
});
|
||||
|
||||
return {
|
||||
...defaultInitializerItem,
|
||||
...(customInitializerItem || {}),
|
||||
} as SchemaInitializerItemType;
|
||||
});
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
export * from './type';
|
||||
export * from './getInitializerItemsByFields';
|
||||
export * from './useCollectionFieldContext';
|
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { ISchema, Schema } from '@formily/json-schema';
|
||||
|
||||
import { CollectionFieldContext } from './useCollectionFieldContext';
|
||||
import { CollectionFieldInterface } from '../../collection-field-interface';
|
||||
import { Collection, CollectionFieldOptions } from '../../collection/Collection';
|
||||
import { InheritanceCollectionMixin } from '../../../collection-manager';
|
||||
|
||||
export interface CollectionFieldDefaultSchema {
|
||||
/**
|
||||
* @default 'string'
|
||||
*/
|
||||
type: string;
|
||||
/**
|
||||
* @default collectionField.name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* @default 'CollectionField'
|
||||
*/
|
||||
'x-component': string;
|
||||
/**
|
||||
* @default `${collection.name}.${collectionField.name}`
|
||||
*/
|
||||
'x-collection-field': string;
|
||||
/**
|
||||
* @default collectionField?.uiSchema?.['x-read-pretty']
|
||||
*/
|
||||
'x-read-pretty'?: boolean;
|
||||
|
||||
/**
|
||||
* @default collectionField?.uiSchema?.title || collectionField.name
|
||||
*/
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface CollectionFieldGetSchemaResult {
|
||||
'x-toolbar'?: string;
|
||||
'x-toolbar-props'?: any;
|
||||
'x-settings'?: string;
|
||||
'x-decorator'?: string;
|
||||
'x-decorator-props'?: any;
|
||||
'x-component-props'?: any;
|
||||
'x-use-component-props'?: string;
|
||||
}
|
||||
|
||||
export interface CollectionFieldDefaultInitializerItem {
|
||||
/**
|
||||
* @default 'item'
|
||||
*/
|
||||
type: string;
|
||||
/**
|
||||
* @default collectionField.name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* @default collectionField?.uiSchema?.title || collectionField.name
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* @default 'CollectionFieldInitializer'
|
||||
*/
|
||||
Component: string;
|
||||
schemaInitialize: (s: ISchema) => void;
|
||||
/**
|
||||
* @default schema
|
||||
*/
|
||||
schema: ISchema;
|
||||
}
|
||||
|
||||
export interface CollectionFieldGetInitializerItemResult {
|
||||
find?: (schema: Schema, key: string, action: string) => any;
|
||||
remove?: (schema: Schema, cb: (schema: Schema, stopProps: Record<string, any>) => void) => void;
|
||||
}
|
||||
|
||||
export interface CommonCollectionFieldsProps {
|
||||
block: string;
|
||||
getSchema: (
|
||||
collectionField: CollectionFieldOptions,
|
||||
context: CollectionFieldContext & {
|
||||
defaultSchema: CollectionFieldDefaultSchema;
|
||||
targetCollection?: Collection;
|
||||
collectionFieldInterface?: CollectionFieldInterface;
|
||||
},
|
||||
) => CollectionFieldGetSchemaResult;
|
||||
isReadPretty?: (context: CollectionFieldContext) => boolean;
|
||||
filter?: (collectionField: CollectionFieldOptions, context: CollectionFieldContext) => boolean;
|
||||
getInitializerItem?: (
|
||||
collectionField: CollectionFieldOptions,
|
||||
context: CollectionFieldContext & {
|
||||
schema: ISchema;
|
||||
defaultInitializerItem: CollectionFieldDefaultInitializerItem;
|
||||
targetCollection?: Collection;
|
||||
collectionFieldInterface?: CollectionFieldInterface;
|
||||
},
|
||||
) => CollectionFieldGetInitializerItemResult;
|
||||
}
|
||||
|
||||
export interface SelfCollectionFieldsProps extends CommonCollectionFieldsProps {
|
||||
context: Omit<CollectionFieldContext, 'collection'> & {
|
||||
collection: Collection;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ParentCollectionFieldsProps extends CommonCollectionFieldsProps {
|
||||
context: Omit<CollectionFieldContext, 'collection'> & {
|
||||
collection: Collection;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AssociationCollectionFieldsProps extends Omit<CommonCollectionFieldsProps, 'filter'> {
|
||||
filterSelfField?: CommonCollectionFieldsProps['filter'];
|
||||
filterAssociationField?: CommonCollectionFieldsProps['filter'];
|
||||
context: Omit<CollectionFieldContext, 'collection'> & {
|
||||
collection: CollectionFieldContext['collection']; // 之前是可选的,这里是必须的
|
||||
};
|
||||
}
|
||||
|
||||
export interface CollectionFieldsProps {
|
||||
/**
|
||||
* Block name.
|
||||
*/
|
||||
block: string;
|
||||
selfField: Omit<SelfCollectionFieldsProps, 'block' | 'context'>;
|
||||
parentField?: Omit<ParentCollectionFieldsProps, 'block' | 'context'>;
|
||||
associationField?: Omit<AssociationCollectionFieldsProps, 'block' | 'context'>;
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { Form } from '@formily/core';
|
||||
import { useFieldSchema, useForm } from '@formily/react';
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
import { TFunction, useTranslation } from 'react-i18next';
|
||||
|
||||
import { DataSource } from '../../data-source/DataSource';
|
||||
import { useCollection } from '../../collection/CollectionProvider';
|
||||
import { useDataSource } from '../../data-source/DataSourceProvider';
|
||||
import { CollectionManager } from '../../collection/CollectionManager';
|
||||
import { DataSourceManager } from '../../data-source/DataSourceManager';
|
||||
import { useActionContext } from '../../../schema-component/antd/action';
|
||||
import { Collection } from '../../collection/Collection';
|
||||
import { useCollectionManager } from '../../collection/CollectionManagerProvider';
|
||||
import { useDataSourceManager } from '../../data-source/DataSourceManagerProvider';
|
||||
import { InheritanceCollectionMixin } from '../../../collection-manager';
|
||||
import { useCompile } from '../../../schema-component';
|
||||
|
||||
export interface CollectionFieldContext {
|
||||
fieldSchema: ISchema;
|
||||
collection?: InheritanceCollectionMixin & Collection;
|
||||
dataSource: DataSource;
|
||||
form: Form<any>;
|
||||
actionContext: ReturnType<typeof useActionContext>;
|
||||
t: TFunction<'translation', undefined>;
|
||||
collectionManager: CollectionManager;
|
||||
dataSourceManager: DataSourceManager;
|
||||
compile: (source: any, ext?: any) => any;
|
||||
targetCollection?: Collection;
|
||||
}
|
||||
|
||||
export function useCollectionFieldContext(): CollectionFieldContext {
|
||||
const { t } = useTranslation();
|
||||
const collection = useCollection<InheritanceCollectionMixin>();
|
||||
const dataSourceManager = useDataSourceManager();
|
||||
const actionContext = useActionContext();
|
||||
const dataSource = useDataSource();
|
||||
const form = useForm();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const collectionManager = useCollectionManager();
|
||||
const compile = useCompile();
|
||||
|
||||
return {
|
||||
t,
|
||||
compile,
|
||||
actionContext,
|
||||
fieldSchema,
|
||||
collection,
|
||||
dataSource,
|
||||
form,
|
||||
collectionManager,
|
||||
dataSourceManager,
|
||||
};
|
||||
}
|
@ -16,3 +16,4 @@ export * from './data-block';
|
||||
export * from './data-source';
|
||||
export * from './collection-record';
|
||||
export * from './utils';
|
||||
export * from './collection-fields-to-initializer-items';
|
||||
|
@ -714,7 +714,7 @@ export const useCustomFormItemInitializerFields = (options?: any) => {
|
||||
});
|
||||
};
|
||||
|
||||
const findSchema = (schema: Schema, key: string, action: string) => {
|
||||
export const findSchema = (schema: Schema, key: string, action: string) => {
|
||||
if (!Schema.isSchemaInstance(schema)) return null;
|
||||
return schema.reduceProperties((buf, s) => {
|
||||
if (s[key] === action) {
|
||||
|
Loading…
Reference in New Issue
Block a user