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 { 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') {
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
},
|
||||
},
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user