feat(calendar): startDate and endDate support the use of association fields (#1397)
* feat(calendar): startDate and endDate support the use of association fields * feat: improve association type
This commit is contained in:
parent
c239a5ad63
commit
65a579384a
@ -1,11 +1,14 @@
|
|||||||
import { clone } from '@formily/shared';
|
import { clone } from '@formily/shared';
|
||||||
|
import { CascaderProps } from 'antd';
|
||||||
import { reduce, unionBy, uniq } from 'lodash';
|
import { reduce, unionBy, uniq } from 'lodash';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
import { useCompile } from '../../schema-component';
|
||||||
import { CollectionManagerContext } from '../context';
|
import { CollectionManagerContext } from '../context';
|
||||||
import { CollectionFieldOptions } from '../types';
|
import { CollectionFieldOptions } from '../types';
|
||||||
|
|
||||||
export const useCollectionManager = () => {
|
export const useCollectionManager = () => {
|
||||||
const { refreshCM, service, interfaces, collections, templates } = useContext(CollectionManagerContext);
|
const { refreshCM, service, interfaces, collections, templates } = useContext(CollectionManagerContext);
|
||||||
|
const compile = useCompile()
|
||||||
const getInheritedFields = (name) => {
|
const getInheritedFields = (name) => {
|
||||||
const inheritKeys = getInheritCollections(name);
|
const inheritKeys = getInheritCollections(name);
|
||||||
const inheritedFields = reduce(
|
const inheritedFields = reduce(
|
||||||
@ -59,25 +62,65 @@ export const useCollectionManager = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getChildrenCollections = (name) => {
|
const getChildrenCollections = (name) => {
|
||||||
const childrens = [];
|
const children = [];
|
||||||
const getChildrens = (name) => {
|
const getChildren = (name) => {
|
||||||
const inheritCollections = collections.filter((v) => {
|
const inheritCollections = collections.filter((v) => {
|
||||||
return v.inherits?.includes(name);
|
return v.inherits?.includes(name);
|
||||||
});
|
});
|
||||||
inheritCollections.forEach((v) => {
|
inheritCollections.forEach((v) => {
|
||||||
const collectionKey = v.name;
|
const collectionKey = v.name;
|
||||||
childrens.push(v);
|
children.push(v);
|
||||||
return getChildrens(collectionKey);
|
return getChildren(collectionKey);
|
||||||
});
|
});
|
||||||
return childrens;
|
return children;
|
||||||
};
|
};
|
||||||
return getChildrens(name);
|
return getChildren(name);
|
||||||
};
|
};
|
||||||
const getCurrentCollectionFields = (name: string) => {
|
const getCurrentCollectionFields = (name: string) => {
|
||||||
const collection = collections?.find((collection) => collection.name === name);
|
const collection = collections?.find((collection) => collection.name === name);
|
||||||
return collection?.fields || [];
|
return collection?.fields || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const getCollectionFieldsOptions = (collectionName: string, type: string | string[] = 'string', opts?: {
|
||||||
|
/**
|
||||||
|
* 为 true 时允许查询所有关联字段
|
||||||
|
* 为 Array<string> 时仅允许查询指定的关联字段
|
||||||
|
*/
|
||||||
|
association?: boolean | string[];
|
||||||
|
}) => {
|
||||||
|
const { association = false } = opts || {};
|
||||||
|
if (typeof type === 'string') {
|
||||||
|
type = [type];
|
||||||
|
}
|
||||||
|
const fields = getCollectionFields(collectionName);
|
||||||
|
const options = fields
|
||||||
|
?.filter(
|
||||||
|
(field) =>
|
||||||
|
field.interface &&
|
||||||
|
(type.includes(field.type) || (association && field.target && field.target !== collectionName &&
|
||||||
|
Array.isArray(association) ? association.includes(field.interface) : false
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
?.map((field) => {
|
||||||
|
const result: CascaderProps<any>['options'][0] = {
|
||||||
|
value: field.name,
|
||||||
|
label: compile(field?.uiSchema?.title) || field.name,
|
||||||
|
};
|
||||||
|
if (association && field.target) {
|
||||||
|
result.children = getCollectionFieldsOptions(field.target, type, opts);
|
||||||
|
if (!result.children?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
// 过滤 map 产生为 null 的数据
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
service,
|
service,
|
||||||
interfaces,
|
interfaces,
|
||||||
@ -91,6 +134,7 @@ export const useCollectionManager = () => {
|
|||||||
getInheritedFields,
|
getInheritedFields,
|
||||||
getCollectionField,
|
getCollectionField,
|
||||||
getCollectionFields,
|
getCollectionFields,
|
||||||
|
getCollectionFieldsOptions,
|
||||||
getCurrentCollectionFields,
|
getCurrentCollectionFields,
|
||||||
getCollection(name: any) {
|
getCollection(name: any) {
|
||||||
if (typeof name !== 'string') {
|
if (typeof name !== 'string') {
|
||||||
|
@ -3,33 +3,19 @@ import React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useCompile, useDesignable } from '../..';
|
import { useCompile, useDesignable } from '../..';
|
||||||
import { useCalendarBlockContext } from '../../../block-provider';
|
import { useCalendarBlockContext } from '../../../block-provider';
|
||||||
import { useCollection } from '../../../collection-manager';
|
import { useCollection, useCollectionManager } from '../../../collection-manager';
|
||||||
import { useCollectionFilterOptions } from '../../../collection-manager/action-hooks';
|
import { useCollectionFilterOptions } from '../../../collection-manager/action-hooks';
|
||||||
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
|
||||||
import { useSchemaTemplate } from '../../../schema-templates';
|
import { useSchemaTemplate } from '../../../schema-templates';
|
||||||
|
|
||||||
const useOptions = (type = 'string') => {
|
|
||||||
const compile = useCompile();
|
|
||||||
const { fields } = useCollection();
|
|
||||||
const options = fields
|
|
||||||
?.filter((field) => field.type === type)
|
|
||||||
?.map((field) => {
|
|
||||||
return {
|
|
||||||
value: field.name,
|
|
||||||
label: compile(field?.uiSchema?.title),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return options;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CalendarDesigner = () => {
|
export const CalendarDesigner = () => {
|
||||||
const field = useField();
|
const field = useField();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const { name, title, fields } = useCollection();
|
const { name, title } = useCollection();
|
||||||
|
const { getCollectionFieldsOptions } = useCollectionManager();
|
||||||
const dataSource = useCollectionFilterOptions(name);
|
const dataSource = useCollectionFilterOptions(name);
|
||||||
const { service } = useCalendarBlockContext();
|
const { service } = useCalendarBlockContext();
|
||||||
const { dn } = useDesignable();
|
const { dn } = useDesignable();
|
||||||
const compile = useCompile();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const template = useSchemaTemplate();
|
const template = useSchemaTemplate();
|
||||||
const defaultFilter = fieldSchema?.['x-decorator-props']?.params?.filter || {};
|
const defaultFilter = fieldSchema?.['x-decorator-props']?.params?.filter || {};
|
||||||
@ -41,7 +27,7 @@ export const CalendarDesigner = () => {
|
|||||||
<SchemaSettings.SelectItem
|
<SchemaSettings.SelectItem
|
||||||
title={t('Title field')}
|
title={t('Title field')}
|
||||||
value={fieldNames.title}
|
value={fieldNames.title}
|
||||||
options={useOptions('string')}
|
options={getCollectionFieldsOptions(name, 'string')}
|
||||||
onChange={(title) => {
|
onChange={(title) => {
|
||||||
const fieldNames = field.decoratorProps.fieldNames || {};
|
const fieldNames = field.decoratorProps.fieldNames || {};
|
||||||
fieldNames['title'] = title;
|
fieldNames['title'] = title;
|
||||||
@ -74,10 +60,12 @@ export const CalendarDesigner = () => {
|
|||||||
dn.refresh();
|
dn.refresh();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<SchemaSettings.SelectItem
|
<SchemaSettings.CascaderItem
|
||||||
title={t('Start date field')}
|
title={t('Start date field')}
|
||||||
value={fieldNames.start}
|
value={fieldNames.start}
|
||||||
options={useOptions('date')}
|
options={getCollectionFieldsOptions(name, 'date', {
|
||||||
|
association: ['o2o', 'obo', 'oho', 'm2o'],
|
||||||
|
})}
|
||||||
onChange={(start) => {
|
onChange={(start) => {
|
||||||
const fieldNames = field.decoratorProps.fieldNames || {};
|
const fieldNames = field.decoratorProps.fieldNames || {};
|
||||||
fieldNames['start'] = start;
|
fieldNames['start'] = start;
|
||||||
@ -93,10 +81,12 @@ export const CalendarDesigner = () => {
|
|||||||
dn.refresh();
|
dn.refresh();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<SchemaSettings.SelectItem
|
<SchemaSettings.CascaderItem
|
||||||
title={t('End date field')}
|
title={t('End date field')}
|
||||||
value={fieldNames.end}
|
value={fieldNames.end}
|
||||||
options={useOptions('date')}
|
options={getCollectionFieldsOptions(name, 'date', {
|
||||||
|
association: ['o2o', 'obo', 'oho', 'm2o'],
|
||||||
|
})}
|
||||||
onChange={(end) => {
|
onChange={(end) => {
|
||||||
const fieldNames = field.decoratorProps.fieldNames || {};
|
const fieldNames = field.decoratorProps.fieldNames || {};
|
||||||
fieldNames['end'] = end;
|
fieldNames['end'] = end;
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
import React, { useContext } from "react";
|
import React, { useContext } from 'react';
|
||||||
import { FormDialog, FormLayout } from "@formily/antd";
|
import { FormDialog, FormLayout } from '@formily/antd';
|
||||||
import { FormOutlined } from '@ant-design/icons';
|
import { FormOutlined } from '@ant-design/icons';
|
||||||
import { SchemaOptionsContext } from "@formily/react";
|
import { SchemaOptionsContext } from '@formily/react';
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { useCollectionManager } from "../../collection-manager";
|
import { useCollection, useCollectionManager } from '../../collection-manager';
|
||||||
import { SchemaComponent, SchemaComponentOptions } from "../../schema-component";
|
import { SchemaComponent, SchemaComponentOptions, useCompile } from '../../schema-component';
|
||||||
import { createCalendarBlockSchema } from "../utils";
|
import { createCalendarBlockSchema } from '../utils';
|
||||||
import { DataBlockInitializer } from "./DataBlockInitializer";
|
import { DataBlockInitializer } from './DataBlockInitializer';
|
||||||
|
import { CascaderProps } from 'antd';
|
||||||
|
|
||||||
export const CalendarBlockInitializer = (props) => {
|
export const CalendarBlockInitializer = (props) => {
|
||||||
const { insert } = props;
|
const { insert } = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { getCollectionFields } = useCollectionManager();
|
const { getCollectionField, getCollectionFieldsOptions } = useCollectionManager();
|
||||||
|
useCollectionManager;
|
||||||
const options = useContext(SchemaOptionsContext);
|
const options = useContext(SchemaOptionsContext);
|
||||||
return (
|
return (
|
||||||
<DataBlockInitializer
|
<DataBlockInitializer
|
||||||
@ -20,23 +22,11 @@ export const CalendarBlockInitializer = (props) => {
|
|||||||
componentType={'Calendar'}
|
componentType={'Calendar'}
|
||||||
icon={<FormOutlined />}
|
icon={<FormOutlined />}
|
||||||
onCreateBlockSchema={async ({ item }) => {
|
onCreateBlockSchema={async ({ item }) => {
|
||||||
const collectionFields = getCollectionFields(item.name);
|
const stringFieldsOptions = getCollectionFieldsOptions(item.name, 'string');
|
||||||
const stringFields = collectionFields
|
const dateFieldsOptions = getCollectionFieldsOptions(item.name, 'date', {
|
||||||
?.filter((field) => field.type === 'string')
|
association: ['o2o', 'obo', 'oho', 'm2o'],
|
||||||
?.map((field) => {
|
|
||||||
return {
|
|
||||||
label: field?.uiSchema?.title,
|
|
||||||
value: field.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const dateFields = collectionFields
|
|
||||||
?.filter((field) => field.type === 'date')
|
|
||||||
?.map((field) => {
|
|
||||||
return {
|
|
||||||
label: field?.uiSchema?.title,
|
|
||||||
value: field.name,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const values = await FormDialog(t('Create calendar block'), () => {
|
const values = await FormDialog(t('Create calendar block'), () => {
|
||||||
return (
|
return (
|
||||||
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
|
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
|
||||||
@ -46,23 +36,23 @@ export const CalendarBlockInitializer = (props) => {
|
|||||||
properties: {
|
properties: {
|
||||||
title: {
|
title: {
|
||||||
title: t('Title field'),
|
title: t('Title field'),
|
||||||
enum: stringFields,
|
enum: stringFieldsOptions,
|
||||||
required: true,
|
required: true,
|
||||||
'x-component': 'Select',
|
'x-component': 'Select',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
},
|
},
|
||||||
start: {
|
start: {
|
||||||
title: t('Start date field'),
|
title: t('Start date field'),
|
||||||
enum: dateFields,
|
enum: dateFieldsOptions,
|
||||||
required: true,
|
required: true,
|
||||||
default: 'createdAt',
|
default: getCollectionField(`${item.name}.createdAt`) ? 'createdAt' : null,
|
||||||
'x-component': 'Select',
|
'x-component': 'Cascader',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
},
|
},
|
||||||
end: {
|
end: {
|
||||||
title: t('End date field'),
|
title: t('End date field'),
|
||||||
enum: dateFields,
|
enum: dateFieldsOptions,
|
||||||
'x-component': 'Select',
|
'x-component': 'Cascader',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -3,7 +3,19 @@ import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd';
|
|||||||
import { createForm, Field, GeneralField } from '@formily/core';
|
import { createForm, Field, GeneralField } from '@formily/core';
|
||||||
import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema, useForm } from '@formily/react';
|
import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema, useForm } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { Alert, Button, Dropdown, Menu, MenuItemProps, Modal, Select, Space, Switch } from 'antd';
|
import {
|
||||||
|
Alert,
|
||||||
|
Button,
|
||||||
|
Cascader,
|
||||||
|
CascaderProps,
|
||||||
|
Dropdown,
|
||||||
|
Menu,
|
||||||
|
MenuItemProps,
|
||||||
|
Modal,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
} from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import React, { createContext, useContext, useMemo, useState } from 'react';
|
import React, { createContext, useContext, useMemo, useState } from 'react';
|
||||||
@ -22,7 +34,7 @@ import {
|
|||||||
useAPIClient,
|
useAPIClient,
|
||||||
useCollection,
|
useCollection,
|
||||||
useCompile,
|
useCompile,
|
||||||
useDesignable
|
useDesignable,
|
||||||
} from '..';
|
} from '..';
|
||||||
import { useSchemaTemplateManager } from '../schema-templates';
|
import { useSchemaTemplateManager } from '../schema-templates';
|
||||||
import { useBlockTemplateContext } from '../schema-templates/BlockTemplate';
|
import { useBlockTemplateContext } from '../schema-templates/BlockTemplate';
|
||||||
@ -61,6 +73,7 @@ type SchemaSettingsNested = {
|
|||||||
Divider?: React.FC;
|
Divider?: React.FC;
|
||||||
Popup?: React.FC<MenuItemProps & { schema?: ISchema }>;
|
Popup?: React.FC<MenuItemProps & { schema?: ISchema }>;
|
||||||
SwitchItem?: React.FC<SwitchItemProps>;
|
SwitchItem?: React.FC<SwitchItemProps>;
|
||||||
|
CascaderItem?: React.FC<CascaderProps<any> & Omit<MenuItemProps, 'title'> & { title: any }>;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -350,7 +363,7 @@ SchemaSettings.Item = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key={key}
|
key={key}
|
||||||
eventKey={eventKey as any || key}
|
eventKey={(eventKey as any) || key}
|
||||||
{...props}
|
{...props}
|
||||||
onClick={(info) => {
|
onClick={(info) => {
|
||||||
info.domEvent.preventDefault();
|
info.domEvent.preventDefault();
|
||||||
@ -434,6 +447,24 @@ SchemaSettings.SelectItem = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SchemaSettings.CascaderItem = (props: CascaderProps<any> & { title: any }) => {
|
||||||
|
const { title, options, value, onChange, ...others } = props;
|
||||||
|
return (
|
||||||
|
<SchemaSettings.Item {...(others as any)}>
|
||||||
|
<div style={{ alignItems: 'center', display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
{title}
|
||||||
|
<Cascader
|
||||||
|
bordered={false}
|
||||||
|
defaultValue={value}
|
||||||
|
onChange={onChange as any}
|
||||||
|
options={options}
|
||||||
|
style={{ textAlign: 'right', minWidth: 100 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SchemaSettings.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
interface SwitchItemProps extends Omit<MenuItemProps, 'onChange'> {
|
interface SwitchItemProps extends Omit<MenuItemProps, 'onChange'> {
|
||||||
title: string;
|
title: string;
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
|
Loading…
Reference in New Issue
Block a user