feat: add calendar block
This commit is contained in:
parent
1e3cdf9572
commit
a7c4abb485
@ -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
|
||||
|
@ -22,6 +22,7 @@ const getSchema = (schema: IField): ISchema => {
|
||||
'x-decorator': 'Form',
|
||||
'x-decorator-props': {
|
||||
initialValue: {
|
||||
interface: schema.name,
|
||||
...schema.default,
|
||||
name: `f_${uid()}`,
|
||||
},
|
||||
|
@ -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) => {
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
};
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
});
|
@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -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>
|
||||
);
|
||||
});
|
@ -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: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,4 +1,5 @@
|
||||
export * from './BlockInitializer';
|
||||
export * from './CalendarActionInitializer';
|
||||
export * from './DetailsActionInitializer';
|
||||
export * from './FormActionInitializer';
|
||||
export * from './FormItemInitializer';
|
||||
|
Loading…
Reference in New Issue
Block a user