improve calendar
This commit is contained in:
parent
f3bdaf641e
commit
dfd4b821ff
@ -30,6 +30,7 @@
|
||||
"@types/mockjs": "^1.0.3",
|
||||
"@types/node": "^12.6.8",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-big-calendar": "^0.33.1",
|
||||
"@types/react-dom": "^17.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.9.1",
|
||||
"@typescript-eslint/parser": "^4.8.2",
|
||||
|
@ -144,6 +144,7 @@ export const loadChinaRegionData = (
|
||||
export const SchemaField = createSchemaField({
|
||||
scope: {
|
||||
Table,
|
||||
Calendar,
|
||||
useAsyncDataSource,
|
||||
ChinaRegion: {
|
||||
loadData: loadChinaRegionData,
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { createContext } from "react";
|
||||
import { Schema } from "@formily/react";
|
||||
|
||||
export const VisibleContext = createContext<any>([]);
|
||||
export const BlockSchemaContext = createContext<Schema>(null);
|
||||
|
@ -70,6 +70,7 @@ import {
|
||||
useDisplayedMapContext,
|
||||
} from '../../constate';
|
||||
import SwitchMenuItem from '../../components/SwitchMenuItem';
|
||||
import { BlockSchemaContext } from '../../context';
|
||||
|
||||
const generateGridBlock = (schema: ISchema) => {
|
||||
const name = schema.name || uid();
|
||||
@ -457,10 +458,129 @@ function generateCardItemSchema(component) {
|
||||
'x-decorator': 'CardItem',
|
||||
default: [],
|
||||
properties: {
|
||||
toolbar: {
|
||||
type: 'void',
|
||||
'x-component': 'Calendar.Toolbar',
|
||||
properties: {
|
||||
today: {
|
||||
type: 'void',
|
||||
title: '今天',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Today',
|
||||
'x-align': 'left',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'today',
|
||||
},
|
||||
},
|
||||
nav: {
|
||||
type: 'void',
|
||||
title: '翻页',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Nav',
|
||||
'x-align': 'left',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'nav',
|
||||
},
|
||||
},
|
||||
title: {
|
||||
type: 'void',
|
||||
title: '标题',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Title',
|
||||
'x-align': 'left',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'title',
|
||||
},
|
||||
},
|
||||
viewSelect: {
|
||||
type: 'void',
|
||||
title: '视图切换',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.ViewSelect',
|
||||
'x-align': 'right',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'viewSelect',
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
type: 'void',
|
||||
title: '过滤',
|
||||
'x-align': 'right',
|
||||
'x-designable-bar': 'Calendar.Filter.DesignableBar',
|
||||
'x-component': 'Calendar.Filter',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'filter',
|
||||
},
|
||||
},
|
||||
create: {
|
||||
type: 'void',
|
||||
title: '新增',
|
||||
'x-align': 'right',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Action',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'create',
|
||||
},
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
},
|
||||
properties: {
|
||||
modal: {
|
||||
type: 'void',
|
||||
title: '新增数据',
|
||||
'x-decorator': 'Form',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-component-props': {
|
||||
useOkAction: '{{ Calendar.useCreateAction }}',
|
||||
},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-component-props': {
|
||||
addNewComponent: 'AddNew.FormItem',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
event: {
|
||||
type: 'void',
|
||||
'x-component': 'Calendar.Event',
|
||||
properties: {},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Tabs',
|
||||
'x-designable-bar': 'Tabs.DesignableBar',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
title: '详情',
|
||||
'x-designable-bar': 'Tabs.TabPane.DesignableBar',
|
||||
'x-component': 'Tabs.TabPane',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-component-props': {
|
||||
addNewComponent: 'AddNew.PaneItem',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -598,7 +718,7 @@ function generateCardItemSchema(component) {
|
||||
},
|
||||
column3: {
|
||||
type: 'void',
|
||||
title: '所属数据表',
|
||||
title: '选择数据源',
|
||||
'x-component': 'Table.Column',
|
||||
properties: {
|
||||
'collection.title': {
|
||||
@ -683,7 +803,7 @@ function generateCardItemSchema(component) {
|
||||
},
|
||||
'collection.title': {
|
||||
type: 'string',
|
||||
title: '所属数据表',
|
||||
title: '选择数据源',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
'x-read-pretty': true,
|
||||
@ -934,7 +1054,7 @@ AddNew.CardItem = observer((props: any) => {
|
||||
<Menu.ItemGroup
|
||||
className={'display-fields'}
|
||||
key={`${view.key}-select`}
|
||||
title={'所属数据表'}
|
||||
title={'选择数据源'}
|
||||
>
|
||||
{collections?.map((item) => (
|
||||
<Menu.Item
|
||||
@ -968,7 +1088,7 @@ AddNew.CardItem = observer((props: any) => {
|
||||
<Menu.ItemGroup
|
||||
className={'display-fields'}
|
||||
key={`${view.key}-select`}
|
||||
title={'所属数据表'}
|
||||
title={'选择数据源'}
|
||||
>
|
||||
{collections?.map((item) => (
|
||||
<Menu.Item
|
||||
@ -982,7 +1102,7 @@ AddNew.CardItem = observer((props: any) => {
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
titleField: {
|
||||
title: {
|
||||
type: 'string',
|
||||
title: '标题字段',
|
||||
required: true,
|
||||
@ -992,12 +1112,12 @@ AddNew.CardItem = observer((props: any) => {
|
||||
(field) => {
|
||||
return {
|
||||
label: field?.uiSchema.title,
|
||||
name: field?.name,
|
||||
value: field?.name,
|
||||
};
|
||||
},
|
||||
),
|
||||
},
|
||||
startTimeField: {
|
||||
start: {
|
||||
title: '开始日期字段',
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
@ -1009,11 +1129,11 @@ AddNew.CardItem = observer((props: any) => {
|
||||
?.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema.title,
|
||||
name: field?.name,
|
||||
value: field?.name,
|
||||
};
|
||||
}),
|
||||
},
|
||||
endTimeField: {
|
||||
end: {
|
||||
title: '结束日期字段',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
@ -1024,7 +1144,7 @@ AddNew.CardItem = observer((props: any) => {
|
||||
?.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema.title,
|
||||
name: field?.name,
|
||||
value: field?.name,
|
||||
};
|
||||
}),
|
||||
},
|
||||
@ -1039,6 +1159,7 @@ AddNew.CardItem = observer((props: any) => {
|
||||
if (schema['key']) {
|
||||
data['key'] = uid();
|
||||
}
|
||||
console.log('fieldNames', values);
|
||||
if (collectionName) {
|
||||
data['x-component-props'] =
|
||||
data['x-component-props'] || {};
|
||||
@ -1046,12 +1167,7 @@ AddNew.CardItem = observer((props: any) => {
|
||||
collectionName;
|
||||
data['x-component-props']['collectionName'] =
|
||||
collectionName;
|
||||
data['x-component-props']['titleField'] =
|
||||
values.titleField;
|
||||
data['x-component-props']['startTimeField'] =
|
||||
values.startTimeField;
|
||||
data['x-component-props']['endTimeField'] =
|
||||
values.endTimeField;
|
||||
data['x-component-props']['fieldNames'] = values;
|
||||
}
|
||||
if (isGridBlock(schema)) {
|
||||
path.pop();
|
||||
@ -1100,7 +1216,7 @@ AddNew.CardItem = observer((props: any) => {
|
||||
<Menu.ItemGroup
|
||||
className={'display-fields'}
|
||||
key={`${view.key}-select`}
|
||||
title={'所属数据表'}
|
||||
title={'选择数据源'}
|
||||
>
|
||||
{collections?.map((item) => (
|
||||
<Menu.SubMenu
|
||||
@ -1159,10 +1275,10 @@ AddNew.CardItem = observer((props: any) => {
|
||||
// </Menu.Item>
|
||||
))}
|
||||
</Menu.ItemGroup>
|
||||
<Menu.Divider></Menu.Divider>
|
||||
{/* <Menu.Divider></Menu.Divider>
|
||||
<Menu.Item icon={<PlusOutlined />} key={`addNew${view.key}`}>
|
||||
新建数据表
|
||||
</Menu.Item>
|
||||
</Menu.Item> */}
|
||||
</Menu.SubMenu>
|
||||
))}
|
||||
</Menu.ItemGroup>
|
||||
@ -1435,7 +1551,9 @@ AddNew.PaneItem = observer((props: any) => {
|
||||
const { schema, insertBefore, insertAfter, appendChild } = useDesignable();
|
||||
const path = useSchemaPath();
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const blockSchema = useContext(BlockSchemaContext);
|
||||
const useResource = `{{ ${blockSchema['x-component']}.useResource }}`;
|
||||
console.log('AddNew.PaneItem.useResource', useResource)
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
@ -1458,8 +1576,7 @@ AddNew.PaneItem = observer((props: any) => {
|
||||
'x-component': 'Form',
|
||||
'x-read-pretty': true,
|
||||
'x-component-props': {
|
||||
useResource: '{{ Table.useResource }}',
|
||||
useValues: '{{ Table.useTableRowRecord }}',
|
||||
useResource,
|
||||
},
|
||||
'x-designable-bar': 'Form.DesignableBar',
|
||||
properties: {
|
||||
@ -1511,8 +1628,7 @@ AddNew.PaneItem = observer((props: any) => {
|
||||
'x-decorator': 'CardItem',
|
||||
'x-component': 'Form',
|
||||
'x-component-props': {
|
||||
useResource: '{{ Table.useResource }}',
|
||||
useValues: '{{ Table.useTableRowRecord }}',
|
||||
useResource,
|
||||
showDefaultButtons: true,
|
||||
},
|
||||
'x-designable-bar': 'Form.DesignableBar',
|
||||
|
85
packages/client/src/schemas/calendar/Action.tsx
Normal file
85
packages/client/src/schemas/calendar/Action.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import React, { useContext, useMemo, useRef, useState } from 'react';
|
||||
import { createForm } from '@formily/core';
|
||||
import {
|
||||
SchemaOptionsContext,
|
||||
Schema,
|
||||
useFieldSchema,
|
||||
observer,
|
||||
SchemaExpressionScopeContext,
|
||||
FormProvider,
|
||||
ISchema,
|
||||
useField,
|
||||
useForm,
|
||||
RecursionField,
|
||||
} from '@formily/react';
|
||||
import {
|
||||
useSchemaPath,
|
||||
SchemaField,
|
||||
useDesignable,
|
||||
removeSchema,
|
||||
updateSchema,
|
||||
} from '../';
|
||||
import get from 'lodash/get';
|
||||
import { Button, Dropdown, Menu, Space } from 'antd';
|
||||
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
|
||||
import cls from 'classnames';
|
||||
import { FormDialog, FormLayout } from '@formily/antd';
|
||||
// import './style.less';
|
||||
import AddNew from '../add-new';
|
||||
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||
import { isGridRowOrCol } from '../grid';
|
||||
import constate from 'constate';
|
||||
import { useEffect } from 'react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { getSchemaPath } from '../../components/schema-renderer';
|
||||
import { useDisplayedMapContext } from '../../constate';
|
||||
import { DragHandle } from '../../components/Sortable';
|
||||
|
||||
export const ActionDesignableBar = (props: any) => {
|
||||
const { schema, remove, refresh, insertAfter } = useDesignable();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const isPopup = Object.keys(schema.properties || {}).length > 0;
|
||||
const displayed = useDisplayedMapContext();
|
||||
const field = useField();
|
||||
return (
|
||||
<div className={cls('designable-bar', { active: visible })}>
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
className={cls('designable-bar-actions', { active: visible })}
|
||||
>
|
||||
<Space size={2}>
|
||||
<DragHandle />
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
visible={visible}
|
||||
onVisibleChange={(visible) => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
onClick={async () => {
|
||||
const displayName =
|
||||
schema?.['x-decorator-props']?.['displayName'];
|
||||
const data = remove();
|
||||
await removeSchema(data);
|
||||
if (displayName) {
|
||||
displayed.remove(displayName);
|
||||
}
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
隐藏
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<MenuOutlined />
|
||||
</Dropdown>
|
||||
</Space>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
300
packages/client/src/schemas/calendar/ActionBar.tsx
Normal file
300
packages/client/src/schemas/calendar/ActionBar.tsx
Normal file
@ -0,0 +1,300 @@
|
||||
import { DndContext, DragOverlay } from '@dnd-kit/core';
|
||||
import { observer, RecursionField, Schema } from '@formily/react';
|
||||
import React, { useState } from 'react';
|
||||
import { createSchema, ISchema, removeSchema, updateSchema } from '..';
|
||||
import {
|
||||
findPropertyByPath,
|
||||
getSchemaPath,
|
||||
useDesignable,
|
||||
} from '../../components/schema-renderer';
|
||||
import { DisplayedMapProvider, useDisplayedMapContext } from '../../constate';
|
||||
import cls from 'classnames';
|
||||
import { Button, Dropdown, Menu, Space } from 'antd';
|
||||
import SwitchMenuItem from '../../components/SwitchMenuItem';
|
||||
import { uid } from '@formily/shared';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Droppable, SortableItem } from '../../components/Sortable';
|
||||
|
||||
export const ActionBar = observer((props: any) => {
|
||||
const { align = 'top' } = props;
|
||||
// const { schema, designable } = useDesignable();
|
||||
const { root, schema, insertAfter, remove, appendChild } = useDesignable();
|
||||
const moveToAfter = (path1, path2, extra = {}) => {
|
||||
if (!path1 || !path2) {
|
||||
return;
|
||||
}
|
||||
if (path1.join('.') === path2.join('.')) {
|
||||
return;
|
||||
}
|
||||
const data = findPropertyByPath(root, path1);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
remove(path1);
|
||||
return insertAfter(
|
||||
{
|
||||
...data.toJSON(),
|
||||
...extra,
|
||||
},
|
||||
path2,
|
||||
);
|
||||
};
|
||||
|
||||
const [dragOverlayContent, setDragOverlayContent] = useState('');
|
||||
return (
|
||||
<DndContext
|
||||
onDragStart={(event) => {
|
||||
setDragOverlayContent(event.active.data?.current?.title || '');
|
||||
// const previewRef = event.active.data?.current?.previewRef;
|
||||
// if (previewRef) {
|
||||
// setDragOverlayContent(previewRef?.current?.innerHTML);
|
||||
// } else {
|
||||
// setDragOverlayContent('');
|
||||
// }
|
||||
}}
|
||||
onDragEnd={async (event) => {
|
||||
const path1 = event.active?.data?.current?.path;
|
||||
const path2 = event.over?.data?.current?.path;
|
||||
const align = event.over?.data?.current?.align;
|
||||
const draggable = event.over?.data?.current?.draggable;
|
||||
if (!path1 || !path2) {
|
||||
return;
|
||||
}
|
||||
if (path1.join('.') === path2.join('.')) {
|
||||
return;
|
||||
}
|
||||
if (!draggable) {
|
||||
console.log('alignalignalignalign', align);
|
||||
const p = findPropertyByPath(root, path1);
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
remove(path1);
|
||||
const data = appendChild(
|
||||
{
|
||||
...p.toJSON(),
|
||||
'x-align': align,
|
||||
},
|
||||
path2,
|
||||
);
|
||||
await updateSchema(data);
|
||||
} else {
|
||||
const data = moveToAfter(path1, path2, {
|
||||
'x-align': align,
|
||||
});
|
||||
await updateSchema(data);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DragOverlay style={{ pointerEvents: 'none', whiteSpace: 'nowrap' }}>
|
||||
{dragOverlayContent}
|
||||
{/* <div style={{ transform: 'translateX(-100%)' }} dangerouslySetInnerHTML={{__html: dragOverlayContent}}></div> */}
|
||||
</DragOverlay>
|
||||
<DisplayedMapProvider>
|
||||
<div className={cls('nb-action-bar', `align-${align}`)}>
|
||||
<div style={{ width: '50%' }}>
|
||||
<Actions align={'left'} />
|
||||
</div>
|
||||
<div style={{ marginLeft: 'auto', width: '50%', textAlign: 'right' }}>
|
||||
<Actions align={'right'} />
|
||||
</div>
|
||||
<AddActionButton />
|
||||
</div>
|
||||
</DisplayedMapProvider>
|
||||
</DndContext>
|
||||
);
|
||||
});
|
||||
|
||||
function generateActionSchema(type) {
|
||||
const actions: { [key: string]: ISchema } = {
|
||||
today: {
|
||||
type: 'void',
|
||||
title: '今天',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Today',
|
||||
'x-align': 'left',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'today',
|
||||
},
|
||||
},
|
||||
nav: {
|
||||
type: 'void',
|
||||
title: '翻页',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Nav',
|
||||
'x-align': 'left',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'nav',
|
||||
},
|
||||
},
|
||||
title: {
|
||||
type: 'void',
|
||||
title: '标题',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Title',
|
||||
'x-align': 'left',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'title',
|
||||
},
|
||||
},
|
||||
viewSelect: {
|
||||
type: 'void',
|
||||
title: '视图切换',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.ViewSelect',
|
||||
'x-align': 'right',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'viewSelect',
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
type: 'void',
|
||||
title: '过滤',
|
||||
'x-align': 'right',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Filter',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'filter',
|
||||
},
|
||||
},
|
||||
create: {
|
||||
type: 'void',
|
||||
title: '新增',
|
||||
'x-align': 'right',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Action',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'create',
|
||||
},
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
},
|
||||
properties: {
|
||||
modal: {
|
||||
type: 'void',
|
||||
title: '新增数据',
|
||||
'x-decorator': 'Form',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-component-props': {
|
||||
useOkAction: '{{ Calendar.useCreateAction }}',
|
||||
},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-component-props': {
|
||||
addNewComponent: 'AddNew.FormItem',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
return actions[type];
|
||||
}
|
||||
|
||||
function AddActionButton() {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const displayed = useDisplayedMapContext();
|
||||
const { appendChild, remove } = useDesignable();
|
||||
const { schema, designable } = useDesignable();
|
||||
if (!designable) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
visible={visible}
|
||||
onVisibleChange={setVisible}
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.ItemGroup title={'操作展示'}>
|
||||
{[
|
||||
{ title: '今天', name: 'today' },
|
||||
{ title: '翻页', name: 'nav' },
|
||||
{ title: '标题', name: 'title' },
|
||||
{ title: '视图切换', name: 'viewSelect' },
|
||||
{ title: '筛选', name: 'filter' },
|
||||
{ title: '新增', name: 'create' },
|
||||
].map((item) => (
|
||||
<SwitchMenuItem
|
||||
key={item.name}
|
||||
checked={displayed.has(item.name)}
|
||||
title={item.title}
|
||||
onChange={async (checked) => {
|
||||
if (!checked) {
|
||||
const s = displayed.get(item.name) as Schema;
|
||||
const path = getSchemaPath(s);
|
||||
displayed.remove(item.name);
|
||||
const removed = remove(path);
|
||||
await removeSchema(removed);
|
||||
} else {
|
||||
const s = generateActionSchema(item.name);
|
||||
const data = appendChild(s);
|
||||
await createSchema(data);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Menu.ItemGroup>
|
||||
<Menu.Divider />
|
||||
<Menu.SubMenu disabled title={'自定义'}>
|
||||
<Menu.Item style={{ minWidth: 120 }}>函数操作</Menu.Item>
|
||||
<Menu.Item>弹窗表单</Menu.Item>
|
||||
<Menu.Item>复杂弹窗</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
className={'designable-btn designable-btn-dash'}
|
||||
style={{ marginLeft: 8 }}
|
||||
type={'dashed'}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
配置工具栏
|
||||
</Button>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
function Actions(props: any) {
|
||||
const { align = 'left' } = props;
|
||||
const { schema, designable } = useDesignable();
|
||||
return (
|
||||
<Droppable
|
||||
id={`${schema.name}-${align}`}
|
||||
className={`action-bar-align-${align}`}
|
||||
data={{ align, path: getSchemaPath(schema) }}
|
||||
>
|
||||
<Space>
|
||||
{schema.mapProperties((s) => {
|
||||
const currentAlign = s['x-align'] || 'left';
|
||||
if (currentAlign !== align) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<SortableItem
|
||||
id={s.name}
|
||||
data={{
|
||||
align,
|
||||
draggable: true,
|
||||
title: s.title,
|
||||
path: getSchemaPath(s),
|
||||
}}
|
||||
>
|
||||
<RecursionField name={s.name} schema={s} />
|
||||
</SortableItem>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
</Droppable>
|
||||
);
|
||||
}
|
@ -12,12 +12,18 @@ import {
|
||||
useForm,
|
||||
RecursionField,
|
||||
} from '@formily/react';
|
||||
import { useSchemaPath, SchemaField, useDesignable, removeSchema } from '../';
|
||||
import {
|
||||
useSchemaPath,
|
||||
SchemaField,
|
||||
useDesignable,
|
||||
removeSchema,
|
||||
updateSchema,
|
||||
} from '../';
|
||||
import get from 'lodash/get';
|
||||
import { Button, Dropdown, Menu, Space } from 'antd';
|
||||
import { Button, Dropdown, Menu, Select, Space } from 'antd';
|
||||
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
|
||||
import cls from 'classnames';
|
||||
import { FormLayout } from '@formily/antd';
|
||||
import { FormDialog, FormLayout } from '@formily/antd';
|
||||
// import './style.less';
|
||||
import AddNew from '../add-new';
|
||||
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||
@ -28,6 +34,8 @@ import { uid } from '@formily/shared';
|
||||
import { getSchemaPath } from '../../components/schema-renderer';
|
||||
import { useCollection, useCollectionContext } from '../../constate';
|
||||
import { useTable } from '../table';
|
||||
import { fieldsToFilterColumns } from './';
|
||||
import { set } from 'lodash';
|
||||
|
||||
export const DesignableBar = observer((props) => {
|
||||
const field = useField();
|
||||
@ -35,11 +43,15 @@ export const DesignableBar = observer((props) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { dragRef } = useContext(DraggableBlockContext);
|
||||
const { props: tableProps } = useTable();
|
||||
const collectionName = field.componentProps?.collectionName || tableProps?.collectionName;
|
||||
const { collection } = useCollection({ collectionName });
|
||||
const collectionName =
|
||||
field.componentProps?.collectionName || tableProps?.collectionName;
|
||||
const { collection, fields } = useCollection({ collectionName });
|
||||
const fieldNames = field.componentProps.fieldNames;
|
||||
return (
|
||||
<div className={cls('designable-bar', { active: visible })}>
|
||||
<div className={'designable-info'}>{collection?.title || collection?.name}</div>
|
||||
<div className={'designable-info'}>
|
||||
{collection?.title || collection?.name}
|
||||
</div>
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@ -57,15 +69,143 @@ export const DesignableBar = observer((props) => {
|
||||
}}
|
||||
overlay={
|
||||
<Menu>
|
||||
{/* <Menu.Item
|
||||
key={'update'}
|
||||
onClick={() => {
|
||||
field.readPretty = true;
|
||||
<Menu.Item>
|
||||
<div
|
||||
style={{ display: 'flex', justifyContent: 'space-between' }}
|
||||
>
|
||||
标题字段:
|
||||
<Select
|
||||
bordered={false}
|
||||
size={'small'}
|
||||
style={{ minWidth: 160 }}
|
||||
value={field.componentProps?.fieldNames.title}
|
||||
onChange={async (title) => {
|
||||
field.componentProps.fieldNames = {
|
||||
...fieldNames,
|
||||
title,
|
||||
};
|
||||
// set(field.componentProps, 'fieldNames.title', value);
|
||||
set(
|
||||
schema['x-component-props'],
|
||||
'fieldNames.title',
|
||||
title,
|
||||
);
|
||||
await updateSchema(schema);
|
||||
}}
|
||||
options={fields?.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema.title,
|
||||
value: field?.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<div
|
||||
style={{ display: 'flex', justifyContent: 'space-between' }}
|
||||
>
|
||||
开始日期字段:
|
||||
<Select
|
||||
bordered={false}
|
||||
size={'small'}
|
||||
style={{ minWidth: 160 }}
|
||||
value={field.componentProps?.fieldNames.start}
|
||||
onChange={async (value) => {
|
||||
field.componentProps.fieldNames = {
|
||||
...fieldNames,
|
||||
start: value,
|
||||
};
|
||||
// set(field.componentProps, 'fieldNames.start', value);
|
||||
set(
|
||||
schema['x-component-props'],
|
||||
'fieldNames.start',
|
||||
value,
|
||||
);
|
||||
await updateSchema(schema);
|
||||
}}
|
||||
options={fields
|
||||
?.filter((field) => field.dataType === 'date')
|
||||
?.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema.title,
|
||||
value: field?.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<div
|
||||
style={{ display: 'flex', justifyContent: 'space-between' }}
|
||||
>
|
||||
结束日期字段:
|
||||
<Select
|
||||
bordered={false}
|
||||
size={'small'}
|
||||
style={{ minWidth: 160 }}
|
||||
value={field.componentProps?.fieldNames.end}
|
||||
onChange={async (value) => {
|
||||
field.componentProps.fieldNames = {
|
||||
...fieldNames,
|
||||
end: value,
|
||||
};
|
||||
set(
|
||||
schema['x-component-props'],
|
||||
'fieldNames.end',
|
||||
value,
|
||||
);
|
||||
await updateSchema(schema);
|
||||
}}
|
||||
options={fields
|
||||
?.filter((field) => field.dataType === 'date')
|
||||
?.map((field) => {
|
||||
return {
|
||||
label: field?.uiSchema.title,
|
||||
value: field?.name,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key={'defaultFilter'}
|
||||
onClick={async () => {
|
||||
const { defaultFilter } = await FormDialog(
|
||||
'设置筛选范围',
|
||||
() => {
|
||||
return (
|
||||
<FormLayout layout={'vertical'}>
|
||||
<SchemaField
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
defaultFilter: {
|
||||
type: 'object',
|
||||
'x-component': 'Filter',
|
||||
properties: fieldsToFilterColumns(fields),
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormLayout>
|
||||
);
|
||||
},
|
||||
).open({
|
||||
initialValues: {
|
||||
defaultFilter:
|
||||
field?.componentProps?.defaultFilter || {},
|
||||
},
|
||||
});
|
||||
schema['x-component-props']['defaultFilter'] =
|
||||
defaultFilter;
|
||||
field.componentProps.defaultFilter = defaultFilter;
|
||||
await updateSchema(schema);
|
||||
}}
|
||||
>
|
||||
编辑表单配置
|
||||
</Menu.Item> */}
|
||||
{/* <Menu.Divider /> */}
|
||||
设置筛选范围
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
key={'delete'}
|
||||
onClick={async () => {
|
||||
|
@ -1,19 +1,128 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import React from 'react';
|
||||
import { SchemaRenderer } from '../../';
|
||||
import { ISchema, SchemaRenderer } from '../../';
|
||||
import { Calendar } from '..';
|
||||
import events from './events';
|
||||
import { uid } from '@formily/shared';
|
||||
|
||||
const schema: ISchema = {
|
||||
type: 'array',
|
||||
name: 'calendar1',
|
||||
'x-component': 'Calendar',
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
title: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
},
|
||||
},
|
||||
default: events,
|
||||
properties: {
|
||||
toolbar: {
|
||||
type: 'void',
|
||||
'x-component': 'Calendar.Toolbar',
|
||||
properties: {
|
||||
today: {
|
||||
type: 'void',
|
||||
title: '今天',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Today',
|
||||
'x-align': 'left',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'today',
|
||||
},
|
||||
},
|
||||
nav: {
|
||||
type: 'void',
|
||||
title: '翻页',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Nav',
|
||||
'x-align': 'left',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'nav',
|
||||
},
|
||||
},
|
||||
title: {
|
||||
type: 'void',
|
||||
title: '标题',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.Title',
|
||||
'x-align': 'left',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'title',
|
||||
},
|
||||
},
|
||||
viewSelect: {
|
||||
type: 'void',
|
||||
title: '视图切换',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Calendar.ViewSelect',
|
||||
'x-align': 'right',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'viewSelect',
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
type: 'void',
|
||||
title: '过滤',
|
||||
'x-align': 'right',
|
||||
'x-designable-bar': 'Calendar.Filter.DesignableBar',
|
||||
'x-component': 'Calendar.Filter',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'filter',
|
||||
},
|
||||
},
|
||||
create: {
|
||||
type: 'void',
|
||||
title: '新增',
|
||||
'x-align': 'right',
|
||||
'x-designable-bar': 'Calendar.ActionDesignableBar',
|
||||
'x-component': 'Action',
|
||||
'x-decorator': 'AddNew.Displayed',
|
||||
'x-decorator-props': {
|
||||
displayName: 'create',
|
||||
},
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
},
|
||||
properties: {
|
||||
modal: {
|
||||
type: 'void',
|
||||
title: '新增数据',
|
||||
'x-decorator': 'Form',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-component-props': {
|
||||
// useOkAction: '{{ Calendar.useTableCreateAction }}',
|
||||
},
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-component-props': {
|
||||
addNewComponent: 'AddNew.FormItem',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
event: {
|
||||
type: 'void',
|
||||
'x-component': 'Calendar.Event',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-component-props': {
|
||||
addNewComponent: 'AddNew.FormItem',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
35
packages/client/src/schemas/calendar/designable.less
Normal file
35
packages/client/src/schemas/calendar/designable.less
Normal file
@ -0,0 +1,35 @@
|
||||
.ant-btn-group {
|
||||
position: relative;
|
||||
&:hover {
|
||||
.designable-bar {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.designable-bar {
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: -1px;
|
||||
left: -1px;
|
||||
bottom: -1px;
|
||||
border-radius: 2px;
|
||||
border: 2px solid #1890ff;
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
.designable-bar-actions {
|
||||
pointer-events: all;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
line-height: 1rem;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
z-index: 10;
|
||||
padding: 0 3px;
|
||||
.anticon {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,12 @@ group:
|
||||
|
||||
<pre lang="tsx">
|
||||
<Calendar>
|
||||
<Calendar.Toolbar>
|
||||
<Calendar.Nav/>
|
||||
<Calendar.Title/>
|
||||
<Calendar.ViewSelect/>
|
||||
<Calendar.Filter/>
|
||||
</Calendar.Toolbar>
|
||||
<Calendar.Event>
|
||||
// 查看日历事件详情
|
||||
</Calendar.Event>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Calendar as BigCalendar,
|
||||
Views,
|
||||
@ -7,11 +7,55 @@ import {
|
||||
import moment from 'moment';
|
||||
import * as dates from 'react-big-calendar/lib/utils/dates';
|
||||
import { navigate } from 'react-big-calendar/lib/utils/constants';
|
||||
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||
import { Space, Button, Radio, Select } from 'antd';
|
||||
import {
|
||||
LeftOutlined,
|
||||
RightOutlined,
|
||||
SettingOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Space,
|
||||
Button,
|
||||
Radio,
|
||||
Select,
|
||||
Popover,
|
||||
Dropdown,
|
||||
Menu,
|
||||
Drawer,
|
||||
} from 'antd';
|
||||
import './style2.less';
|
||||
import { observer, useField } from '@formily/react';
|
||||
import './designable.less';
|
||||
|
||||
import {
|
||||
FormProvider,
|
||||
observer,
|
||||
RecursionField,
|
||||
Schema,
|
||||
useField,
|
||||
useForm,
|
||||
} from '@formily/react';
|
||||
import { DesignableBar } from './DesignableBar';
|
||||
import { createContext } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { SchemaField, useDesignable } from '../../components/schema-renderer';
|
||||
import { ActionBar } from './ActionBar';
|
||||
import { ActionDesignableBar } from './Action';
|
||||
import { createForm } from '@formily/core';
|
||||
import { flatten, get } from 'lodash';
|
||||
import {
|
||||
CollectionProvider,
|
||||
useCollectionContext,
|
||||
useDisplayedMapContext,
|
||||
} from '../../constate';
|
||||
import { FormButtonGroup, FormDialog, FormLayout, Submit } from '@formily/antd';
|
||||
import IconPicker from '../../components/icon-picker';
|
||||
import { DragHandle } from '../../components/Sortable';
|
||||
import SwitchMenuItem from '../../components/SwitchMenuItem';
|
||||
import { removeSchema, updateSchema } from '..';
|
||||
import { MenuOutlined } from '@ant-design/icons';
|
||||
import { interfaces } from '../database-field/interfaces';
|
||||
import cls from 'classnames';
|
||||
import { Resource } from '../../resource';
|
||||
import { useRequest } from 'ahooks';
|
||||
|
||||
const localizer = momentLocalizer(moment);
|
||||
|
||||
@ -28,6 +72,10 @@ interface ToolbarProps {
|
||||
onView?: (view: string) => void;
|
||||
}
|
||||
|
||||
export const RecordContext = createContext(null);
|
||||
export const ToolbarContext = createContext<ToolbarProps>(null);
|
||||
export const CalendarContext = createContext(null);
|
||||
|
||||
function Toolbar(props: ToolbarProps) {
|
||||
const {
|
||||
label,
|
||||
@ -37,99 +85,508 @@ function Toolbar(props: ToolbarProps) {
|
||||
onView,
|
||||
localizer: { messages },
|
||||
} = props;
|
||||
const { schema } = useDesignable();
|
||||
const toolBarSchema: Schema = schema.reduceProperties((buf, current) => {
|
||||
if (current['x-component'] === 'Calendar.Toolbar') {
|
||||
return current;
|
||||
}
|
||||
return buf;
|
||||
}, null);
|
||||
|
||||
return (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Space>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onNavigate(navigate.TODAY);
|
||||
}}
|
||||
>
|
||||
今天
|
||||
</Button>
|
||||
<div className="ant-btn-group">
|
||||
<Button
|
||||
icon={<LeftOutlined />}
|
||||
onClick={() => onNavigate(navigate.PREVIOUS)}
|
||||
></Button>
|
||||
<Button
|
||||
icon={<RightOutlined />}
|
||||
onClick={() => onNavigate(navigate.NEXT)}
|
||||
></Button>
|
||||
</div>
|
||||
<div style={{ fontSize: '1.75em', fontWeight: 300 }}>{label}</div>
|
||||
</Space>
|
||||
<Space style={{ float: 'right' }}>
|
||||
<Select value={view} onChange={onView}>
|
||||
{views.map((name) => (
|
||||
<Select.Option value={name}>{messages[name]}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Button>筛选</Button>
|
||||
<Button type={'primary'}>新增</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<ToolbarContext.Provider value={props}>
|
||||
<RecursionField name={toolBarSchema.name} schema={toolBarSchema} />
|
||||
</ToolbarContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
const messages: any = {
|
||||
allDay: '',
|
||||
previous: (
|
||||
<div>
|
||||
<LeftOutlined />
|
||||
</div>
|
||||
),
|
||||
next: (
|
||||
<div>
|
||||
<RightOutlined />
|
||||
</div>
|
||||
),
|
||||
today: '今天',
|
||||
month: '月',
|
||||
week: '周',
|
||||
work_week: '工作日',
|
||||
day: '天',
|
||||
agenda: '列表',
|
||||
date: '日期',
|
||||
time: '时间',
|
||||
event: '事件',
|
||||
noEventsInRange: '无',
|
||||
showMore: (count) => `还有 ${count} 项`,
|
||||
};
|
||||
|
||||
const useCalendar = () => {
|
||||
return useContext(CalendarContext);
|
||||
};
|
||||
|
||||
const toEvents = (data: any[], fieldNames: any) => {
|
||||
return data?.map((item) => {
|
||||
return {
|
||||
id: get(item, fieldNames.id || 'id'),
|
||||
title: get(item, fieldNames.title) || '无标题',
|
||||
start: get(item, fieldNames.start),
|
||||
end: get(item, fieldNames.end || fieldNames.start),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const Calendar: any = observer((props: any) => {
|
||||
const field = useField<Formily.Core.Models.ArrayField>();
|
||||
const { collectionName, fieldNames = {} } = props;
|
||||
console.log('Calendar', props);
|
||||
const { schema } = useDesignable();
|
||||
const eventSchema: Schema = schema.reduceProperties((buf, current) => {
|
||||
if (current['x-component'] === 'Calendar.Event') {
|
||||
return current;
|
||||
}
|
||||
return buf;
|
||||
}, null);
|
||||
const resource = Resource.make(collectionName);
|
||||
const service = useRequest(
|
||||
(params) => {
|
||||
if (!collectionName) {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
return resource.list({
|
||||
...params,
|
||||
perPage: -1,
|
||||
});
|
||||
},
|
||||
{
|
||||
formatResult: (data) => data?.data,
|
||||
onSuccess(data) {
|
||||
const events = toEvents(data, fieldNames) || [];
|
||||
// console.log('events', events);
|
||||
field.setValue(events);
|
||||
},
|
||||
// refreshDeps: [props.fieldNames],
|
||||
},
|
||||
);
|
||||
useEffect(() => {
|
||||
service.run({
|
||||
defaultFilter: props?.defaultFilter,
|
||||
sort: props?.fieldNames?.start || 'created_at',
|
||||
});
|
||||
}, [props?.fieldNames, props.defaultFilter]);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [record, setRecord] = useState<any>({});
|
||||
console.log('field.value', field.value);
|
||||
return (
|
||||
<div style={{ height: 700 }}>
|
||||
<BigCalendar
|
||||
popup
|
||||
selectable
|
||||
events={field.value || []}
|
||||
views={allViews}
|
||||
step={60}
|
||||
showMultiDayTimes
|
||||
messages={{
|
||||
allDay: '',
|
||||
previous: (
|
||||
<div>
|
||||
<LeftOutlined />
|
||||
</div>
|
||||
),
|
||||
next: (
|
||||
<div>
|
||||
<RightOutlined />
|
||||
</div>
|
||||
),
|
||||
today: '今天',
|
||||
month: '月',
|
||||
week: '周',
|
||||
work_week: '工作日',
|
||||
day: '天',
|
||||
agenda: '列表',
|
||||
date: '日期',
|
||||
time: '时间',
|
||||
event: '事件',
|
||||
noEventsInRange: '无',
|
||||
showMore: (count) => `还有 ${count} 项`,
|
||||
}}
|
||||
formats={{
|
||||
monthHeaderFormat: 'Y年M月',
|
||||
agendaDateFormat: 'M月DD日',
|
||||
dayHeaderFormat: 'Y年M月DD日',
|
||||
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
|
||||
if (dates.eq(start, end, 'month')) {
|
||||
return local.format(start, 'Y年M月', culture);
|
||||
}
|
||||
return `${local.format(start, 'Y年M月', culture)} - ${local.format(
|
||||
end,
|
||||
'Y年M月',
|
||||
culture,
|
||||
)}`;
|
||||
},
|
||||
}}
|
||||
defaultDate={new Date(2015, 3, 1)}
|
||||
components={{
|
||||
toolbar: Toolbar,
|
||||
}}
|
||||
localizer={localizer}
|
||||
/>
|
||||
<CollectionProvider collectionName={props.collectionName}>
|
||||
<CalendarContext.Provider value={{ resource, service, props }}>
|
||||
<div {...props} style={{ height: 700 }}>
|
||||
<Drawer
|
||||
width={'50%'}
|
||||
visible={visible}
|
||||
onClose={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
title={'查看数据'}
|
||||
bodyStyle={{
|
||||
background: '#f0f2f5',
|
||||
paddingTop: 0,
|
||||
}}
|
||||
>
|
||||
<RecordContext.Provider value={record}>
|
||||
<RecursionField
|
||||
name={eventSchema.name}
|
||||
schema={eventSchema}
|
||||
onlyRenderProperties
|
||||
/>
|
||||
</RecordContext.Provider>
|
||||
</Drawer>
|
||||
<BigCalendar
|
||||
popup
|
||||
selectable
|
||||
events={Array.isArray(field.value.slice()) ? field.value.slice() : []}
|
||||
views={allViews}
|
||||
step={60}
|
||||
showMultiDayTimes
|
||||
messages={messages}
|
||||
onSelectSlot={(slotInfo) => {
|
||||
console.log('onSelectSlot', slotInfo);
|
||||
}}
|
||||
onDoubleClickEvent={(event) => {
|
||||
console.log('onDoubleClickEvent');
|
||||
}}
|
||||
onSelectEvent={(event) => {
|
||||
const record = field.value?.find((item) => item.id === event.id);
|
||||
if (!record) {
|
||||
return;
|
||||
}
|
||||
setRecord(record);
|
||||
setVisible(true);
|
||||
console.log('onSelectEvent');
|
||||
}}
|
||||
formats={{
|
||||
monthHeaderFormat: 'Y年M月',
|
||||
agendaDateFormat: 'M月DD日',
|
||||
dayHeaderFormat: 'Y年M月DD日',
|
||||
dayRangeHeaderFormat: ({ start, end }, culture, local) => {
|
||||
if (dates.eq(start, end, 'month')) {
|
||||
return local.format(start, 'Y年M月', culture);
|
||||
}
|
||||
return `${local.format(
|
||||
start,
|
||||
'Y年M月',
|
||||
culture,
|
||||
)} - ${local.format(end, 'Y年M月', culture)}`;
|
||||
},
|
||||
}}
|
||||
defaultDate={new Date()}
|
||||
components={{
|
||||
toolbar: Toolbar,
|
||||
}}
|
||||
localizer={localizer}
|
||||
/>
|
||||
</div>
|
||||
</CalendarContext.Provider>
|
||||
</CollectionProvider>
|
||||
);
|
||||
});
|
||||
|
||||
Calendar.useCreateAction = () => {
|
||||
const { service, resource } = useCalendar();
|
||||
const form = useForm();
|
||||
return {
|
||||
async run() {
|
||||
await resource.create(form.values);
|
||||
await form.reset();
|
||||
return service.refresh();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
Calendar.useResource = ({ onSuccess }) => {
|
||||
const { props } = useCalendar();
|
||||
const { collection } = useCollectionContext();
|
||||
const record = useContext(RecordContext);
|
||||
const resource = Resource.make({
|
||||
resourceName: collection?.name || props.collectionName,
|
||||
resourceKey: record['id'],
|
||||
});
|
||||
console.log(
|
||||
'collection?.name || props.collectionName',
|
||||
collection?.name || props.collectionName,
|
||||
);
|
||||
const service = useRequest(
|
||||
(params?: any) => {
|
||||
console.log('Table.useResource', params);
|
||||
return resource.get(params);
|
||||
},
|
||||
{
|
||||
formatResult: (result) => result?.data,
|
||||
onSuccess,
|
||||
refreshDeps: [record],
|
||||
// manual,
|
||||
},
|
||||
);
|
||||
return { resource, service, initialValues: service.data, ...service };
|
||||
};
|
||||
|
||||
Calendar.Toolbar = ActionBar;
|
||||
|
||||
Calendar.Title = observer(() => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const { label } = useContext(ToolbarContext);
|
||||
return (
|
||||
<div
|
||||
className="ant-btn-group"
|
||||
style={{ fontSize: '1.75em', fontWeight: 300 }}
|
||||
>
|
||||
{label}
|
||||
<DesignableBar />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Calendar.Today = observer((props) => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const { onNavigate } = useContext(ToolbarContext);
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onNavigate(navigate.TODAY);
|
||||
}}
|
||||
>
|
||||
今天
|
||||
<DesignableBar />
|
||||
</Button>
|
||||
);
|
||||
});
|
||||
|
||||
Calendar.Nav = observer((props) => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const { onNavigate } = useContext(ToolbarContext);
|
||||
return (
|
||||
<div className="ant-btn-group">
|
||||
<Button
|
||||
icon={<LeftOutlined />}
|
||||
onClick={() => onNavigate(navigate.PREVIOUS)}
|
||||
></Button>
|
||||
<Button
|
||||
icon={<RightOutlined />}
|
||||
onClick={() => onNavigate(navigate.NEXT)}
|
||||
></Button>
|
||||
<DesignableBar />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Calendar.ViewSelect = observer((props) => {
|
||||
const { DesignableBar } = useDesignable();
|
||||
const {
|
||||
views,
|
||||
view,
|
||||
onView,
|
||||
localizer: { messages },
|
||||
} = useContext(ToolbarContext);
|
||||
return (
|
||||
<div className="ant-btn-group">
|
||||
<Select value={view} onChange={onView}>
|
||||
{views.map((name) => (
|
||||
<Select.Option value={name}>{messages[name]}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<DesignableBar />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const fieldsToFilterColumns = (fields: any[], options: any = {}) => {
|
||||
const { fieldNames = [] } = options;
|
||||
const properties = {};
|
||||
fields.forEach((field, index) => {
|
||||
if (fieldNames?.length && !fieldNames.includes(field.name)) {
|
||||
return;
|
||||
}
|
||||
const fieldOption = interfaces.get(field.interface);
|
||||
if (!fieldOption.operations) {
|
||||
return;
|
||||
}
|
||||
properties[`column${index}`] = {
|
||||
type: 'void',
|
||||
title: field?.uiSchema?.title,
|
||||
'x-component': 'Filter.Column',
|
||||
'x-component-props': {
|
||||
operations: fieldOption.operations,
|
||||
},
|
||||
properties: {
|
||||
[field.name]: {
|
||||
...field.uiSchema,
|
||||
'x-decorator': 'FormilyFormItem',
|
||||
title: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
return properties;
|
||||
};
|
||||
|
||||
Calendar.Filter = observer((props: any) => {
|
||||
const { service } = useCalendar();
|
||||
const { fieldNames = [] } = props;
|
||||
const { schema, DesignableBar } = useDesignable();
|
||||
const form = useMemo(() => createForm(), []);
|
||||
const { fields = [] } = useCollectionContext();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const obj = flatten(form.values.filter || {});
|
||||
console.log('flatten', obj, Object.values(obj));
|
||||
const count = Object.values(obj).filter((i) =>
|
||||
Array.isArray(i) ? i.length : i,
|
||||
).length;
|
||||
const icon = props.icon || 'FilterOutlined';
|
||||
const properties = fieldsToFilterColumns(fields, { fieldNames });
|
||||
schema.mapProperties((p) => {
|
||||
properties[p.name] = p;
|
||||
});
|
||||
return (
|
||||
<Popover
|
||||
trigger={['click']}
|
||||
placement={'bottomLeft'}
|
||||
visible={visible}
|
||||
onVisibleChange={setVisible}
|
||||
content={
|
||||
<div>
|
||||
<FormProvider form={form}>
|
||||
<SchemaField
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
filter: {
|
||||
type: 'object',
|
||||
'x-component': 'Filter',
|
||||
properties,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<FormButtonGroup align={'right'}>
|
||||
<Submit
|
||||
onSubmit={() => {
|
||||
const { filter } = form.values;
|
||||
console.log('Table.Filter', form.values);
|
||||
setVisible(false);
|
||||
// return service.run({
|
||||
// ...service.params[0],
|
||||
// filter,
|
||||
// });
|
||||
}}
|
||||
>
|
||||
提交
|
||||
</Submit>
|
||||
</FormButtonGroup>
|
||||
</FormProvider>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button icon={<IconPicker type={icon} />}>
|
||||
{count > 0 ? `${count} 个筛选项` : schema.title}
|
||||
<DesignableBar />
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
Calendar.Filter.DesignableBar = () => {
|
||||
const { schema, remove, refresh, insertAfter } = useDesignable();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const displayed = useDisplayedMapContext();
|
||||
const { fields } = useCollectionContext();
|
||||
const field = useField();
|
||||
let fieldNames = field.componentProps.fieldNames || [];
|
||||
if (fieldNames.length === 0) {
|
||||
fieldNames = fields.map((field) => field.name);
|
||||
}
|
||||
return (
|
||||
<div className={cls('designable-bar', { active: visible })}>
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
className={cls('designable-bar-actions', { active: visible })}
|
||||
>
|
||||
<Space>
|
||||
<DragHandle />
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
visible={visible}
|
||||
onVisibleChange={(visible) => {
|
||||
setVisible(visible);
|
||||
}}
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.ItemGroup title={'筛选字段'}>
|
||||
{fields
|
||||
.filter((collectionField) => {
|
||||
const option = interfaces.get(collectionField.interface);
|
||||
return option.operations?.length;
|
||||
})
|
||||
.map((collectionField) => (
|
||||
<SwitchMenuItem
|
||||
title={collectionField?.uiSchema?.title}
|
||||
checked={fieldNames.includes(collectionField.name)}
|
||||
onChange={async (checked) => {
|
||||
if (checked) {
|
||||
fieldNames.push(collectionField.name);
|
||||
} else {
|
||||
const index = fieldNames.indexOf(
|
||||
collectionField.name,
|
||||
);
|
||||
if (index > -1) {
|
||||
fieldNames.splice(index, 1);
|
||||
}
|
||||
}
|
||||
console.log({ fieldNames, field });
|
||||
schema['x-component-props']['fieldNames'] =
|
||||
fieldNames;
|
||||
field.componentProps.fieldNames = fieldNames;
|
||||
updateSchema(schema);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Menu.ItemGroup>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
onClick={async (e) => {
|
||||
setVisible(false);
|
||||
const values = await FormDialog('修改名称和图标', () => {
|
||||
return (
|
||||
<FormLayout layout={'vertical'}>
|
||||
<SchemaField
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
title: '按钮名称',
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
icon: {
|
||||
type: 'string',
|
||||
title: '按钮图标',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'IconPicker',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormLayout>
|
||||
);
|
||||
}).open({
|
||||
initialValues: {
|
||||
title: schema['title'],
|
||||
icon: schema['x-component-props']?.['icon'],
|
||||
},
|
||||
});
|
||||
schema['title'] = values.title;
|
||||
schema['x-component-props']['icon'] = values.icon;
|
||||
field.componentProps.icon = values.icon;
|
||||
field.title = values.title;
|
||||
updateSchema(schema);
|
||||
refresh();
|
||||
}}
|
||||
>
|
||||
修改名称和图标
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
onClick={async () => {
|
||||
const displayName =
|
||||
schema?.['x-decorator-props']?.['displayName'];
|
||||
const data = remove();
|
||||
await removeSchema(data);
|
||||
if (displayName) {
|
||||
displayed.remove(displayName);
|
||||
}
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
移除
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<MenuOutlined />
|
||||
</Dropdown>
|
||||
</Space>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Calendar.DesignableBar = DesignableBar;
|
||||
|
||||
Calendar.ActionDesignableBar = ActionDesignableBar;
|
||||
|
@ -20,13 +20,17 @@ import { useDesignable } from '../';
|
||||
import { AddNew } from '../add-new';
|
||||
import { BlockItem } from '../block-item';
|
||||
import { DraggableBlockContext } from '../../components/drag-and-drop';
|
||||
import { BlockSchemaContext } from '../../context';
|
||||
|
||||
export const CardItem: any = connect((props) => {
|
||||
const { schema } = useDesignable();
|
||||
return (
|
||||
<BlockItem>
|
||||
<Card bordered={false} {...props}>
|
||||
{props.children}
|
||||
</Card>
|
||||
</BlockItem>
|
||||
<BlockSchemaContext.Provider value={schema}>
|
||||
<BlockItem>
|
||||
<Card bordered={false} {...props}>
|
||||
{props.children}
|
||||
</Card>
|
||||
</BlockItem>
|
||||
</BlockSchemaContext.Provider>
|
||||
);
|
||||
});
|
||||
|
@ -35,8 +35,9 @@ export const DesignableBar = observer((props) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { dragRef } = useContext(DraggableBlockContext);
|
||||
const { props: tableProps } = useTable();
|
||||
const collectionName = field.componentProps?.collectionName || tableProps?.collectionName;
|
||||
const { collection } = useCollection({ collectionName });
|
||||
const { collection } = useCollectionContext();
|
||||
// const collectionName = field.componentProps?.collectionName || tableProps?.collectionName;
|
||||
// const { collection } = useCollection({ collectionName });
|
||||
return (
|
||||
<div className={cls('designable-bar', { active: visible })}>
|
||||
<div className={'designable-info'}>{collection?.title || collection?.name}</div>
|
||||
|
@ -61,7 +61,7 @@ const FormMain = (props: any) => {
|
||||
const { resource, run, service } = useResource({
|
||||
onSuccess: (initialValues: any) => {
|
||||
console.log('onSuccess', { initialValues });
|
||||
// form.setInitialValues(initialValues);
|
||||
form.setInitialValues(initialValues);
|
||||
form.setValues(initialValues);
|
||||
},
|
||||
});
|
||||
|
@ -3864,6 +3864,14 @@
|
||||
resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
|
||||
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
|
||||
|
||||
"@types/react-big-calendar@^0.33.1":
|
||||
version "0.33.1"
|
||||
resolved "https://registry.npmjs.org/@types/react-big-calendar/-/react-big-calendar-0.33.1.tgz#48463e5b7d27a82dbf5808dea529c03708514b31"
|
||||
integrity sha512-xX5BaxS+7K62+gtEeg0DqaBInNXDSk2LgkHhbgtqjo/CqwEqB8cwuhIE34XnL33z+pzbn/h7i+0W5fJq0kShdQ==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-dom@^16.9.8", "@types/react-dom@^17.0.0", "@types/react-dom@^17.0.3":
|
||||
version "17.0.9"
|
||||
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz#441a981da9d7be117042e1a6fd3dac4b30f55add"
|
||||
|
Loading…
Reference in New Issue
Block a user