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:
Dunqing 2023-02-01 12:55:46 +08:00 committed by GitHub
parent c239a5ad63
commit 65a579384a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 63 deletions

View File

@ -1,11 +1,14 @@
import { clone } from '@formily/shared';
import { CascaderProps } from 'antd';
import { reduce, unionBy, uniq } from 'lodash';
import { useContext } from 'react';
import { useCompile } from '../../schema-component';
import { CollectionManagerContext } from '../context';
import { CollectionFieldOptions } from '../types';
export const useCollectionManager = () => {
const { refreshCM, service, interfaces, collections, templates } = useContext(CollectionManagerContext);
const compile = useCompile()
const getInheritedFields = (name) => {
const inheritKeys = getInheritCollections(name);
const inheritedFields = reduce(
@ -59,25 +62,65 @@ export const useCollectionManager = () => {
};
const getChildrenCollections = (name) => {
const childrens = [];
const getChildrens = (name) => {
const children = [];
const getChildren = (name) => {
const inheritCollections = collections.filter((v) => {
return v.inherits?.includes(name);
});
inheritCollections.forEach((v) => {
const collectionKey = v.name;
childrens.push(v);
return getChildrens(collectionKey);
children.push(v);
return getChildren(collectionKey);
});
return childrens;
return children;
};
return getChildrens(name);
return getChildren(name);
};
const getCurrentCollectionFields = (name: string) => {
const collection = collections?.find((collection) => collection.name === name);
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 {
service,
interfaces,
@ -91,6 +134,7 @@ export const useCollectionManager = () => {
getInheritedFields,
getCollectionField,
getCollectionFields,
getCollectionFieldsOptions,
getCurrentCollectionFields,
getCollection(name: any) {
if (typeof name !== 'string') {

View File

@ -3,33 +3,19 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useCompile, useDesignable } from '../..';
import { useCalendarBlockContext } from '../../../block-provider';
import { useCollection } from '../../../collection-manager';
import { useCollection, useCollectionManager } from '../../../collection-manager';
import { useCollectionFilterOptions } from '../../../collection-manager/action-hooks';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
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 = () => {
const field = useField();
const fieldSchema = useFieldSchema();
const { name, title, fields } = useCollection();
const { name, title } = useCollection();
const { getCollectionFieldsOptions } = useCollectionManager();
const dataSource = useCollectionFilterOptions(name);
const { service } = useCalendarBlockContext();
const { dn } = useDesignable();
const compile = useCompile();
const { t } = useTranslation();
const template = useSchemaTemplate();
const defaultFilter = fieldSchema?.['x-decorator-props']?.params?.filter || {};
@ -41,7 +27,7 @@ export const CalendarDesigner = () => {
<SchemaSettings.SelectItem
title={t('Title field')}
value={fieldNames.title}
options={useOptions('string')}
options={getCollectionFieldsOptions(name, 'string')}
onChange={(title) => {
const fieldNames = field.decoratorProps.fieldNames || {};
fieldNames['title'] = title;
@ -74,10 +60,12 @@ export const CalendarDesigner = () => {
dn.refresh();
}}
/>
<SchemaSettings.SelectItem
<SchemaSettings.CascaderItem
title={t('Start date field')}
value={fieldNames.start}
options={useOptions('date')}
options={getCollectionFieldsOptions(name, 'date', {
association: ['o2o', 'obo', 'oho', 'm2o'],
})}
onChange={(start) => {
const fieldNames = field.decoratorProps.fieldNames || {};
fieldNames['start'] = start;
@ -93,10 +81,12 @@ export const CalendarDesigner = () => {
dn.refresh();
}}
/>
<SchemaSettings.SelectItem
<SchemaSettings.CascaderItem
title={t('End date field')}
value={fieldNames.end}
options={useOptions('date')}
options={getCollectionFieldsOptions(name, 'date', {
association: ['o2o', 'obo', 'oho', 'm2o'],
})}
onChange={(end) => {
const fieldNames = field.decoratorProps.fieldNames || {};
fieldNames['end'] = end;

View File

@ -1,18 +1,20 @@
import React, { useContext } from "react";
import { FormDialog, FormLayout } from "@formily/antd";
import React, { useContext } from 'react';
import { FormDialog, FormLayout } from '@formily/antd';
import { FormOutlined } from '@ant-design/icons';
import { SchemaOptionsContext } from "@formily/react";
import { useTranslation } from "react-i18next";
import { SchemaOptionsContext } from '@formily/react';
import { useTranslation } from 'react-i18next';
import { useCollectionManager } from "../../collection-manager";
import { SchemaComponent, SchemaComponentOptions } from "../../schema-component";
import { createCalendarBlockSchema } from "../utils";
import { DataBlockInitializer } from "./DataBlockInitializer";
import { useCollection, useCollectionManager } from '../../collection-manager';
import { SchemaComponent, SchemaComponentOptions, useCompile } from '../../schema-component';
import { createCalendarBlockSchema } from '../utils';
import { DataBlockInitializer } from './DataBlockInitializer';
import { CascaderProps } from 'antd';
export const CalendarBlockInitializer = (props) => {
const { insert } = props;
const { t } = useTranslation();
const { getCollectionFields } = useCollectionManager();
const { getCollectionField, getCollectionFieldsOptions } = useCollectionManager();
useCollectionManager;
const options = useContext(SchemaOptionsContext);
return (
<DataBlockInitializer
@ -20,23 +22,11 @@ export const CalendarBlockInitializer = (props) => {
componentType={'Calendar'}
icon={<FormOutlined />}
onCreateBlockSchema={async ({ item }) => {
const collectionFields = getCollectionFields(item.name);
const stringFields = collectionFields
?.filter((field) => field.type === 'string')
?.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 stringFieldsOptions = getCollectionFieldsOptions(item.name, 'string');
const dateFieldsOptions = getCollectionFieldsOptions(item.name, 'date', {
association: ['o2o', 'obo', 'oho', 'm2o'],
});
const values = await FormDialog(t('Create calendar block'), () => {
return (
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
@ -46,23 +36,23 @@ export const CalendarBlockInitializer = (props) => {
properties: {
title: {
title: t('Title field'),
enum: stringFields,
enum: stringFieldsOptions,
required: true,
'x-component': 'Select',
'x-decorator': 'FormItem',
},
start: {
title: t('Start date field'),
enum: dateFields,
enum: dateFieldsOptions,
required: true,
default: 'createdAt',
'x-component': 'Select',
default: getCollectionField(`${item.name}.createdAt`) ? 'createdAt' : null,
'x-component': 'Cascader',
'x-decorator': 'FormItem',
},
end: {
title: t('End date field'),
enum: dateFields,
'x-component': 'Select',
enum: dateFieldsOptions,
'x-component': 'Cascader',
'x-decorator': 'FormItem',
},
},

View File

@ -3,7 +3,19 @@ import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd';
import { createForm, Field, GeneralField } from '@formily/core';
import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema, useForm } from '@formily/react';
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 { cloneDeep } from 'lodash';
import React, { createContext, useContext, useMemo, useState } from 'react';
@ -22,7 +34,7 @@ import {
useAPIClient,
useCollection,
useCompile,
useDesignable
useDesignable,
} from '..';
import { useSchemaTemplateManager } from '../schema-templates';
import { useBlockTemplateContext } from '../schema-templates/BlockTemplate';
@ -61,6 +73,7 @@ type SchemaSettingsNested = {
Divider?: React.FC;
Popup?: React.FC<MenuItemProps & { schema?: ISchema }>;
SwitchItem?: React.FC<SwitchItemProps>;
CascaderItem?: React.FC<CascaderProps<any> & Omit<MenuItemProps, 'title'> & { title: any }>;
[key: string]: any;
};
@ -350,7 +363,7 @@ SchemaSettings.Item = (props) => {
return (
<Menu.Item
key={key}
eventKey={eventKey as any || key}
eventKey={(eventKey as any) || key}
{...props}
onClick={(info) => {
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'> {
title: string;
checked?: boolean;