feat: add calendar block

This commit is contained in:
chenos 2022-02-21 18:40:44 +08:00
parent 1e3cdf9572
commit a7c4abb485
10 changed files with 389 additions and 31 deletions

View File

@ -1,5 +1,5 @@
import { Board } from '@nocobase/client';
import { Button } from 'antd';
import { Button, Card } from 'antd';
import React, { useState } from 'react';
import '../style.less';
@ -94,6 +94,9 @@ export default function App() {
cardAdderPosition={'bottom'}
onNewCardConfirm={(draftCard) => draftCard}
onCardNew={console.log}
renderCard={(card, { column, dragging }) => {
return <Card style={{ width: 220, marginBottom: 15 }}>{card.title}</Card>;
}}
renderCardAdder={({ column }) => {
return (
<Button

View File

@ -22,6 +22,7 @@ const getSchema = (schema: IField): ISchema => {
'x-decorator': 'Form',
'x-decorator-props': {
initialValue: {
interface: schema.name,
...schema.default,
name: `f_${uid()}`,
},

View File

@ -17,8 +17,8 @@ export const ActionBar = observer((props: any) => {
);
}
return (
<div style={{ display: 'flex', justifyContent: 'space-between', ...style }} {...others}>
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', ...style }} {...others}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
<DndContext>
<Space>
{fieldSchema.mapProperties((schema, key) => {

View File

@ -31,10 +31,10 @@ export const ActionInitializer = observer((props: any) => {
},
{
type: 'item',
title: t('turn page'),
title: t('Turn page'),
component: InitializeAction,
schema: {
title: t('turn page'),
title: t('Turn page'),
'x-component': 'Calendar.Nav',
'x-action': `calendar:nav`,
'x-align': 'left',

View File

@ -5,9 +5,8 @@ import moment from 'moment';
import React, { useState } from 'react';
import { Calendar as BigCalendar, momentLocalizer } from 'react-big-calendar';
import * as dates from 'react-big-calendar/lib/utils/dates';
import { useTranslation } from 'react-i18next';
import { SchemaComponent } from '../..';
import { AsyncDataProvider, useRequest } from '../../../';
import { AsyncDataProvider, RecordProvider, useRequest } from '../../../';
import { i18n } from '../../../i18n';
import { ActionBar, ActionContext } from '../action';
import { ActionInitializer } from './ActionInitializer';
@ -78,7 +77,7 @@ const useRequestProps = (props) => {
};
};
const useDefDataSource = (props, options) => {
const useDefDataSource = (options, props) => {
return useRequest(useRequestProps(props), options);
};
@ -92,20 +91,24 @@ export const Calendar: any = observer((props: any) => {
end: 'end',
},
} = props;
const { t } = useTranslation();
const field = useField<VoidField>();
const fieldSchema = useFieldSchema();
const [dataSource, setDataSource] = useState(props.dataSource || []);
const [visible, setVisible] = useState(false);
const [record, setRecord] = useState<any>({});
const result = useDataSource(props, {
uid: fieldSchema['x-uid'],
refreshDeps: [props.dataSource],
onSuccess(data) {
return toEvents(data?.data, fieldNames);
const result = useDataSource(
{
uid: fieldSchema['x-uid'],
refreshDeps: [props.dataSource],
onSuccess(data) {
const events = toEvents(data?.data, fieldNames);
setDataSource(events);
},
},
});
props,
);
const eventSchema: Schema = fieldSchema.reduceProperties((buf, current) => {
if (current['x-component'] === 'Calendar.Event') {
return current;
@ -118,12 +121,14 @@ export const Calendar: any = observer((props: any) => {
<CalendarContext.Provider value={{ field, props, record }}>
<div {...props} style={{ height: 700 }}>
<ActionContext.Provider value={{ visible, setVisible }}>
<SchemaComponent memoized name={eventSchema.name} schema={eventSchema as any} />
<RecordProvider record={record}>
<SchemaComponent memoized name={eventSchema.name} schema={eventSchema as any} />
</RecordProvider>
</ActionContext.Provider>
<BigCalendar
popup
selectable
events={Array.isArray(result?.data?.data) ? result?.data?.data : []}
events={dataSource}
views={['month', 'week', 'day']}
step={60}
showMultiDayTimes
@ -141,7 +146,7 @@ export const Calendar: any = observer((props: any) => {
}
setRecord(record);
setVisible(true);
console.log('onSelectEvent');
console.log('onSelectEvent', record);
}}
formats={{
monthHeaderFormat: 'Y-M',

View File

@ -0,0 +1,163 @@
import { FormOutlined } from '@ant-design/icons';
import { FormDialog, FormLayout } from '@formily/antd';
import { ISchema, SchemaOptionsContext } from '@formily/react';
import React, { useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../..';
import { useCollectionManager } from '../../../collection-manager';
import { SchemaComponent, SchemaComponentOptions } from '../../../schema-component';
const createSchema = (collectionName, { title, start, end }) => {
const schema: ISchema = {
type: 'void',
'x-collection': 'collections',
'x-decorator': 'ResourceActionProvider',
'x-decorator-props': {
collection: collectionName,
request: {
resource: collectionName,
action: 'list',
params: {},
},
},
'x-designer': 'Form.Designer',
'x-component': 'CardItem',
properties: {
calendar: {
type: 'void',
'x-component': 'Calendar',
'x-component-props': {
useDataSource: '{{ cm.useDataSourceFromRAC }}',
fieldNames: {
title,
start,
end,
},
},
properties: {
toolBar: {
type: 'void',
'x-component': 'Calendar.ActionBar',
'x-action-initializer': 'CalendarActionInitializer',
properties: {},
},
event: {
type: 'void',
name: 'event',
'x-component': 'Calendar.Event',
properties: {
drawer: {
type: 'void',
'x-decorator': 'Form',
'x-decorator-props': {
useValues: '{{ cm.useValuesFromRecord }}',
},
'x-component': 'Action.Drawer',
title: '{{ t("Edit record") }}',
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-item-initializer': 'FormItemInitializer',
properties: {},
},
footer: {
type: 'void',
'x-component': 'Action.Drawer.Footer',
properties: {
actions: {
type: 'void',
'x-action-initializer': 'PopupFormActionInitializer',
'x-decorator': 'DndContext',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
},
properties: {},
},
},
},
},
},
},
},
},
},
},
};
return schema;
};
const itemWrap = SchemaInitializer.itemWrap;
export const CalendarBlock = itemWrap((props) => {
const { insert } = props;
const { collections, getCollection } = useCollectionManager();
const { t } = useTranslation();
const options = useContext(SchemaOptionsContext);
return (
<SchemaInitializer.Item
{...props}
icon={<FormOutlined />}
onClick={async ({ item }) => {
const collection = getCollection(item.name);
const fieldEnum = collection?.fields?.map((field) => {
return {
label: field?.uiSchema?.title,
value: field.name,
};
});
const values = await FormDialog('创建日历区块', () => {
return (
<SchemaComponentOptions scope={options.scope} components={{ ...options.components }}>
<FormLayout layout={'vertical'}>
<SchemaComponent
schema={{
properties: {
title: {
title: '标题字段',
enum: fieldEnum,
required: true,
'x-component': 'Select',
'x-decorator': 'FormItem',
},
start: {
title: '开始日期字段',
enum: fieldEnum,
required: true,
'x-component': 'Select',
'x-decorator': 'FormItem',
},
end: {
title: '结束日期字段',
enum: fieldEnum,
'x-component': 'Select',
'x-decorator': 'FormItem',
},
},
}}
/>
</FormLayout>
</SchemaComponentOptions>
);
}).open({
initialValues: {},
});
insert(createSchema(item.name, values));
}}
items={[
{
type: 'itemGroup',
title: t('Select data source'),
children: collections?.map((item) => {
return {
type: 'item',
name: item.name,
title: item.title,
};
}),
},
]}
/>
);
});

View File

@ -3,6 +3,7 @@ import { uid } from '@formily/shared';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../..';
import { CalendarBlock } from './CalendarBlock';
import { FormBlock } from './FormBlock';
import { MarkdownBlock } from './MarkdownBlock';
import { TableBlock } from './TableBlock';
@ -44,6 +45,11 @@ export const BlockInitializer = observer((props: any) => {
title: t('Form'),
component: FormBlock,
},
{
type: 'item',
title: t('Calendar'),
component: CalendarBlock,
},
],
},
{

View File

@ -0,0 +1,187 @@
import { observer, useFieldSchema } from '@formily/react';
import { Switch } from 'antd';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../..';
import { useDesignable } from '../../../schema-component';
const useCurrentActionSchema = (action: string) => {
const fieldSchema = useFieldSchema();
const { remove } = useDesignable();
const schema = fieldSchema.reduceProperties((buf, s) => {
if (s['x-action'] === action) {
return s;
}
return buf;
});
return {
schema,
exists: !!schema,
remove() {
schema && remove(schema);
},
};
};
const InitializeAction = SchemaInitializer.itemWrap((props) => {
const { item, insert } = props;
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
return (
<SchemaInitializer.Item
onClick={() => {
if (exists) {
return remove();
}
insert({
type: 'void',
'x-designer': 'Action.Designer',
'x-component': 'Action',
...item.schema,
});
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
{item.title}
<Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
</div>
</SchemaInitializer.Item>
);
});
export const CalendarActionInitializer = observer((props: any) => {
const { t } = useTranslation();
return (
<SchemaInitializer.Button
insertPosition={'beforeEnd'}
style={{ marginLeft: 8 }}
items={[
{
type: 'itemGroup',
title: t('Enable actions'),
children: [
{
type: 'item',
title: t('Today'),
component: InitializeAction,
schema: {
title: t('Today'),
'x-component': 'Calendar.Today',
'x-action': `calendar:today`,
'x-align': 'left',
},
},
{
type: 'item',
title: t('Turn page'),
component: InitializeAction,
schema: {
title: t('Turn page'),
'x-component': 'Calendar.Nav',
'x-action': `calendar:nav`,
'x-align': 'left',
},
},
{
type: 'item',
title: t('Title'),
component: InitializeAction,
schema: {
title: t('Title'),
'x-component': 'Calendar.Title',
'x-action': `calendar:title`,
'x-align': 'left',
},
},
{
type: 'item',
title: t('Select view'),
component: InitializeAction,
schema: {
title: t('Select view'),
'x-component': 'Calendar.ViewSelect',
'x-action': `calendar:viewSelect`,
'x-align': 'left',
},
},
{
type: 'item',
title: t('Filter'),
component: InitializeAction,
schema: {
title: '{{ t("Filter") }}',
'x-action': 'filter',
'x-align': 'left',
},
},
{
type: 'item',
title: t('Add new'),
component: InitializeAction,
schema: {
type: 'void',
title: '{{ t("Add new") }}',
'x-action': 'create',
'x-component': 'Action',
'x-component-props': {
type: 'primary',
openMode: 'drawer',
},
properties: {
drawer: {
type: 'void',
title: '{{ t("Add new record") }}',
'x-component': 'Action.Container',
'x-component-props': {},
'x-decorator': 'Form',
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-item-initializer': 'FormItemInitializer',
properties: {},
},
footer: {
type: 'void',
'x-component': 'Action.Container.Footer',
properties: {
actions: {
type: 'void',
'x-action-initializer': 'PopupFormActionInitializer',
'x-decorator': 'DndContext',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
},
properties: {},
},
},
},
},
},
},
},
},
{
type: 'item',
title: t('Delete'),
component: InitializeAction,
schema: {
title: '{{ t("Delete") }}',
'x-action': 'destroy',
'x-component-props': {
confirm: {
title: "{{t('Delete record')}}",
content: "{{t('Are you sure you want to delete it?')}}",
},
useAction: '{{ cm.useBulkDestroyAction }}',
},
},
},
],
},
]}
>
{t('Configure actions')}
</SchemaInitializer.Button>
);
});

View File

@ -200,21 +200,13 @@ export const TableRecordActionInitializer = observer((props: any) => {
properties: {
actions: {
type: 'void',
// 'x-action-initializer': 'DrawerForm',
'x-action-initializer': 'PopupFormActionInitializer',
'x-decorator': 'DndContext',
'x-component': 'ActionBar',
'x-component-props': {},
properties: {
actions: {
type: 'void',
'x-action-initializer': 'PopupFormActionInitializer',
'x-decorator': 'DndContext',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
},
properties: {},
},
'x-component-props': {
layout: 'one-column',
},
properties: {},
},
},
},

View File

@ -1,4 +1,5 @@
export * from './BlockInitializer';
export * from './CalendarActionInitializer';
export * from './DetailsActionInitializer';
export * from './FormActionInitializer';
export * from './FormItemInitializer';