improve calendar and kanban

This commit is contained in:
chenos 2021-08-18 23:34:35 +08:00
parent 3e5e9c194c
commit f3bdaf641e
8 changed files with 657 additions and 119 deletions

View File

@ -30,6 +30,7 @@ import { Space, Card, Modal, Spin } from 'antd';
import { ArrayTable } from '../../schemas/array-table'; import { ArrayTable } from '../../schemas/array-table';
import { Action } from '../../schemas/action'; import { Action } from '../../schemas/action';
import { AddNew } from '../../schemas/add-new'; import { AddNew } from '../../schemas/add-new';
import { Calendar } from '../../schemas/calendar';
import { Cascader } from '../../schemas/cascader'; import { Cascader } from '../../schemas/cascader';
import { Checkbox } from '../../schemas/checkbox'; import { Checkbox } from '../../schemas/checkbox';
import { ColorSelect } from '../../schemas/color-select'; import { ColorSelect } from '../../schemas/color-select';
@ -46,6 +47,7 @@ import { IconPicker } from '../../schemas/icon-picker';
import { Input } from '../../schemas/input'; import { Input } from '../../schemas/input';
import { InputNumber } from '../../schemas/input-number'; import { InputNumber } from '../../schemas/input-number';
import { Markdown } from '../../schemas/markdown'; import { Markdown } from '../../schemas/markdown';
import { Kanban } from '../../schemas/kanban';
import { Menu } from '../../schemas/menu'; import { Menu } from '../../schemas/menu';
import { Password } from '../../schemas/password'; import { Password } from '../../schemas/password';
import { Radio } from '../../schemas/radio'; import { Radio } from '../../schemas/radio';
@ -170,6 +172,7 @@ export const SchemaField = createSchemaField({
Action, Action,
AddNew, AddNew,
Calendar,
Cascader, Cascader,
Checkbox, Checkbox,
ColorSelect, ColorSelect,
@ -182,6 +185,7 @@ export const SchemaField = createSchemaField({
IconPicker, IconPicker,
Input, Input,
InputNumber, InputNumber,
Kanban,
Markdown, Markdown,
Menu, Menu,
Password, Password,

View File

@ -347,6 +347,123 @@ function generateCardItemSchema(component) {
}, },
}, },
}, },
Kanban: {
type: 'array',
name: 'kanban1',
'x-decorator': 'CardItem',
'x-decorator-props': {
style: {
background: 'none',
},
bodyStyle: {
padding: 0,
},
},
'x-designable-bar': 'Kanban.DesignableBar',
'x-component': 'Kanban',
default: [
{
id: '1',
type: 'A',
title: 'A1',
},
{
id: '2',
type: 'A',
title: 'A2',
},
{
id: '3',
type: 'A',
title: 'A3',
},
{
id: '4',
type: 'B',
title: 'B4',
},
{
id: '5',
type: 'B',
title: 'B5',
},
{
id: '6',
type: 'B',
title: 'B6',
},
{
id: '7',
type: 'C',
title: 'C7',
},
{
id: '8',
type: 'C',
title: 'C8',
},
{
id: '9',
type: 'C',
title: 'C9',
},
],
properties: {
card1: {
type: 'void',
'x-component': 'Kanban.Card',
properties: {
item1: {
type: 'void',
'x-component': 'Kanban.Item',
properties: {
title: {
type: 'string',
// title: '标题',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
view1: {
type: 'void',
'x-component': 'Kanban.Card.View',
properties: {
item1: {
type: 'void',
'x-component': 'Kanban.Item',
properties: {
title: {
type: 'string',
title: '标题',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
},
Calendar: {
type: 'array',
name: 'calendar1',
'x-component': 'Calendar',
'x-designable-bar': 'Calendar.DesignableBar',
'x-decorator': 'CardItem',
default: [],
properties: {
event: {
type: 'void',
'x-component': 'Calendar.Event',
properties: {},
},
},
},
'Chart.Column': { 'Chart.Column': {
type: 'void', type: 'void',
'x-decorator': 'CardItem', 'x-decorator': 'CardItem',
@ -733,6 +850,12 @@ AddNew.CardItem = observer((props: any) => {
overlay={ overlay={
<Menu <Menu
onClick={async (info) => { onClick={async (info) => {
if (info.key.startsWith('Calendar.')) {
return;
}
if (info.key.startsWith('Kanban.')) {
return;
}
let data: ISchema; let data: ISchema;
let collectionName = null; let collectionName = null;
let isNew = false; let isNew = false;
@ -801,22 +924,10 @@ AddNew.CardItem = observer((props: any) => {
{[ {[
{ key: 'Table', title: '表格', icon: 'TableOutlined' }, { key: 'Table', title: '表格', icon: 'TableOutlined' },
{ key: 'Form', title: '表单', icon: 'FormOutlined' }, { key: 'Form', title: '表单', icon: 'FormOutlined' },
{
key: 'Calendar',
title: '日历',
icon: 'CalendarOutlined',
disabled: true,
},
{
key: 'Kanban',
title: '看板',
icon: 'CreditCardOutlined',
disabled: true,
},
].map((view) => ( ].map((view) => (
<Menu.SubMenu <Menu.SubMenu
icon={<IconPicker type={view.icon} />} icon={<IconPicker type={view.icon} />}
disabled={view.disabled} // disabled={view.disabled}
key={view.key} key={view.key}
title={view.title} title={view.title}
> >
@ -840,6 +951,220 @@ AddNew.CardItem = observer((props: any) => {
</Menu.Item> </Menu.Item>
</Menu.SubMenu> </Menu.SubMenu>
))} ))}
{[
{
key: 'Calendar',
title: '日历',
icon: 'CalendarOutlined',
// disabled: true,
},
].map((view) => (
<Menu.SubMenu
icon={<IconPicker type={view.icon} />}
// disabled={view.disabled}
key={view.key}
title={view.title}
>
<Menu.ItemGroup
className={'display-fields'}
key={`${view.key}-select`}
title={'所属数据表'}
>
{collections?.map((item) => (
<Menu.Item
style={{ minWidth: 150 }}
key={`Calendar.collection.${item.name}.${view.key}`}
onClick={async () => {
const values = await FormDialog(`日历配置`, () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
titleField: {
type: 'string',
title: '标题字段',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Select',
enum: item?.generalFields?.map(
(field) => {
return {
label: field?.uiSchema.title,
name: field?.name,
};
},
),
},
startTimeField: {
title: '开始日期字段',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Select',
enum: item?.generalFields
?.filter(
(field) => field.dataType === 'date',
)
?.map((field) => {
return {
label: field?.uiSchema.title,
name: field?.name,
};
}),
},
endTimeField: {
title: '结束日期字段',
'x-decorator': 'FormItem',
'x-component': 'Select',
enum: item?.generalFields
?.filter(
(field) => field.dataType === 'date',
)
?.map((field) => {
return {
label: field?.uiSchema.title,
name: field?.name,
};
}),
},
},
}}
/>
</FormLayout>
);
}).open({});
let data = generateCardItemSchema('Calendar');
const collectionName = item.name;
if (schema['key']) {
data['key'] = uid();
}
if (collectionName) {
data['x-component-props'] =
data['x-component-props'] || {};
data['x-component-props']['resource'] =
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;
}
if (isGridBlock(schema)) {
path.pop();
path.pop();
data = generateGridBlock(data);
} else if (isGrid(schema)) {
data = generateGridBlock(data);
}
if (data) {
let s;
if (isGrid(schema)) {
s = appendChild(data, [...path]);
} else if (defaultAction === 'insertAfter') {
s = insertAfter(data, [...path]);
} else {
s = insertBefore(data, [...path]);
}
await createSchema(s);
}
}}
>
{item.title}
</Menu.Item>
))}
</Menu.ItemGroup>
{/* <Menu.Divider></Menu.Divider> */}
{/* <Menu.Item icon={<PlusOutlined />} key={`addNew${view.key}`}>
</Menu.Item> */}
</Menu.SubMenu>
))}
{[
{
key: 'Kanban',
title: '看板',
icon: 'CreditCardOutlined',
// disabled: true,
},
].map((view) => (
<Menu.SubMenu
icon={<IconPicker type={view.icon} />}
// disabled={view.disabled}
key={view.key}
title={view.title}
>
<Menu.ItemGroup
className={'display-fields'}
key={`${view.key}-select`}
title={'所属数据表'}
>
{collections?.map((item) => (
<Menu.SubMenu
// style={{ minWidth: 150 }}
key={`collection.${item.name}.${view.key}`}
title={item.title}
>
<Menu.ItemGroup title={'选择分组字段'}>
<Menu.Item
style={{ minWidth: 150 }}
key={`Kanban.collection.${item.name}.${view.key}`}
onClick={async () => {
let data = generateCardItemSchema('Kanban');
const collectionName = item.name;
if (schema['key']) {
data['key'] = uid();
}
if (collectionName) {
data['x-component-props'] =
data['x-component-props'] || {};
data['x-component-props']['resource'] =
collectionName;
data['x-component-props']['collectionName'] =
collectionName;
// data['x-component-props']['groupField'] = '';
}
if (isGridBlock(schema)) {
path.pop();
path.pop();
data = generateGridBlock(data);
} else if (isGrid(schema)) {
data = generateGridBlock(data);
}
if (data) {
let s;
if (isGrid(schema)) {
s = appendChild(data, [...path]);
} else if (defaultAction === 'insertAfter') {
s = insertAfter(data, [...path]);
} else {
s = insertBefore(data, [...path]);
}
await createSchema(s);
}
}}
>
1
</Menu.Item>
</Menu.ItemGroup>
</Menu.SubMenu>
// <Menu.Item
// style={{ minWidth: 150 }}
// key={`collection.${item.name}.${view.key}`}
// >
// {item.title}
// </Menu.Item>
))}
</Menu.ItemGroup>
<Menu.Divider></Menu.Divider>
<Menu.Item icon={<PlusOutlined />} key={`addNew${view.key}`}>
</Menu.Item>
</Menu.SubMenu>
))}
</Menu.ItemGroup> </Menu.ItemGroup>
<Menu.Divider /> <Menu.Divider />
<Menu.ItemGroup title={'多媒体区块'}> <Menu.ItemGroup title={'多媒体区块'}>
@ -887,7 +1212,7 @@ AddNew.CardItem = observer((props: any) => {
</Menu.Item> </Menu.Item>
</Menu.ItemGroup> </Menu.ItemGroup>
<Menu.Divider /> <Menu.Divider />
<Menu.SubMenu key={'Ref'} title={'引用模板'}> <Menu.SubMenu key={'Ref'} icon={<LinkOutlined />} title={'引用模板'}>
<Menu.ItemGroup key={'form-select'} title={'选择模板'}> <Menu.ItemGroup key={'form-select'} title={'选择模板'}>
<Menu.Item key={'Ref.ActionLogs'}></Menu.Item> <Menu.Item key={'Ref.ActionLogs'}></Menu.Item>
</Menu.ItemGroup> </Menu.ItemGroup>

View File

@ -0,0 +1,91 @@
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 } 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 { 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 { useCollection, useCollectionContext } from '../../constate';
import { useTable } from '../table';
export const DesignableBar = observer((props) => {
const field = useField();
const { designable, schema, refresh, deepRemove } = useDesignable();
const [visible, setVisible] = useState(false);
const { dragRef } = useContext(DraggableBlockContext);
const { props: tableProps } = useTable();
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>
<span
onClick={(e) => {
e.stopPropagation();
}}
className={cls('designable-bar-actions', { active: visible })}
>
<Space size={2}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
{/* <Menu.Item
key={'update'}
onClick={() => {
field.readPretty = true;
}}
>
</Menu.Item> */}
{/* <Menu.Divider /> */}
<Menu.Item
key={'delete'}
onClick={async () => {
const removed = deepRemove();
// console.log({ removed })
const last = removed.pop();
if (isGridRowOrCol(last)) {
await removeSchema(last);
}
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);
});

View File

@ -11,6 +11,7 @@ import { LeftOutlined, RightOutlined } from '@ant-design/icons';
import { Space, Button, Radio, Select } from 'antd'; import { Space, Button, Radio, Select } from 'antd';
import './style2.less'; import './style2.less';
import { observer, useField } from '@formily/react'; import { observer, useField } from '@formily/react';
import { DesignableBar } from './DesignableBar';
const localizer = momentLocalizer(moment); const localizer = momentLocalizer(moment);
@ -71,7 +72,7 @@ function Toolbar(props: ToolbarProps) {
); );
} }
export const Calendar = observer((props: any) => { export const Calendar: any = observer((props: any) => {
const field = useField<Formily.Core.Models.ArrayField>(); const field = useField<Formily.Core.Models.ArrayField>();
return ( return (
<div style={{ height: 700 }}> <div style={{ height: 700 }}>
@ -130,3 +131,5 @@ export const Calendar = observer((props: any) => {
</div> </div>
); );
}); });
Calendar.DesignableBar = DesignableBar;

View File

@ -0,0 +1,91 @@
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 } 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 { 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 { useCollection, useCollectionContext } from '../../constate';
import { useTable } from '../table';
export const DesignableBar = observer((props) => {
const field = useField();
const { designable, schema, refresh, deepRemove } = useDesignable();
const [visible, setVisible] = useState(false);
const { dragRef } = useContext(DraggableBlockContext);
const { props: tableProps } = useTable();
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>
<span
onClick={(e) => {
e.stopPropagation();
}}
className={cls('designable-bar-actions', { active: visible })}
>
<Space size={2}>
<AddNew.CardItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
{/* <Menu.Item
key={'update'}
onClick={() => {
field.readPretty = true;
}}
>
</Menu.Item> */}
{/* <Menu.Divider /> */}
<Menu.Item
key={'delete'}
onClick={async () => {
const removed = deepRemove();
// console.log({ removed })
const last = removed.pop();
if (isGridRowOrCol(last)) {
await removeSchema(last);
}
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);
});

View File

@ -22,6 +22,7 @@ import { createContext } from 'react';
import { useContext } from 'react'; import { useContext } from 'react';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import { DesignableBar } from './DesignableBar';
export function SortableItem(props) { export function SortableItem(props) {
const { const {
@ -95,6 +96,10 @@ export const Kanban: any = observer(() => {
return prev; return prev;
}, null); }, null);
return ( return (
<div>
{/* <div>
<Button></Button>
</div> */}
<div className={'kanban'}> <div className={'kanban'}>
<DndContext <DndContext
// collisionDetection={closestCorners} // collisionDetection={closestCorners}
@ -199,6 +204,7 @@ export const Kanban: any = observer(() => {
</SortableContext> </SortableContext>
</DndContext> </DndContext>
</div> </div>
</div>
); );
}); });
@ -232,3 +238,5 @@ Kanban.Card = observer((props) => {
Kanban.Card.View = observer((props) => { Kanban.Card.View = observer((props) => {
return <div>{props.children}</div>; return <div>{props.children}</div>;
}); });
Kanban.DesignableBar = DesignableBar;

View File

@ -2,7 +2,7 @@
position: relative; position: relative;
width: 25vw; width: 25vw;
background: #f9f9f9; background: #f9f9f9;
margin: 0 1%; margin: 0 12px;
padding: 24px; padding: 24px;
} }
@ -14,6 +14,7 @@
.kanban { .kanban {
display: flex; display: flex;
margin: 0 -12px;
} }
.item { .item {

View File

@ -374,6 +374,12 @@ const useTableColumns = () => {
const columnSchemas = schema.reduceProperties((columns, current) => { const columnSchemas = schema.reduceProperties((columns, current) => {
if (isColumn(current)) { if (isColumn(current)) {
if (current['x-hidden']) {
return columns;
}
if (current['x-display'] && current['x-display'] !== 'visible') {
return columns;
}
return [...columns, current]; return [...columns, current];
} }
return [...columns]; return [...columns];
@ -1409,6 +1415,7 @@ Table.Filter.DesignableBar = () => {
<Menu.Divider /> <Menu.Divider />
<Menu.Item <Menu.Item
onClick={async (e) => { onClick={async (e) => {
setVisible(false);
const values = await FormDialog('修改名称和图标', () => { const values = await FormDialog('修改名称和图标', () => {
return ( return (
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
@ -1531,6 +1538,7 @@ Table.ExportActionDesignableBar = () => {
<Menu.Divider /> <Menu.Divider />
<Menu.Item <Menu.Item
onClick={async (e) => { onClick={async (e) => {
setVisible(false);
const values = await FormDialog('修改名称和图标', () => { const values = await FormDialog('修改名称和图标', () => {
return ( return (
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
@ -1721,6 +1729,7 @@ Table.Action.DesignableBar = () => {
<Menu> <Menu>
<Menu.Item <Menu.Item
onClick={async (e) => { onClick={async (e) => {
setVisible(false);
const values = await FormDialog('修改名称和图标', () => { const values = await FormDialog('修改名称和图标', () => {
return ( return (
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
@ -1901,6 +1910,7 @@ Table.Column.DesignableBar = () => {
<Menu> <Menu>
<Menu.Item <Menu.Item
onClick={async (e) => { onClick={async (e) => {
setVisible(false);
const values = await FormDialog('修改列标题', () => { const values = await FormDialog('修改列标题', () => {
return ( return (
<FormLayout layout={'vertical'}> <FormLayout layout={'vertical'}>
@ -1940,7 +1950,6 @@ Table.Column.DesignableBar = () => {
key: schema['key'], key: schema['key'],
title: title, title: title,
}); });
setVisible(false);
}} }}
> >
@ -1948,14 +1957,20 @@ Table.Column.DesignableBar = () => {
<Menu.Divider /> <Menu.Divider />
<Menu.Item <Menu.Item
onClick={async () => { onClick={async () => {
const s = remove();
const fieldName = const fieldName =
schema['x-component-props']?.['fieldName']; schema['x-component-props']?.['fieldName'];
displayed.remove(fieldName); displayed.remove(fieldName);
await removeSchema(s); schema['x-hidden'] = true;
refresh();
await updateSchema({
key: schema['key'],
['x-hidden']: true,
});
// const s = remove();
// await removeSchema(s);
}} }}
> >
</Menu.Item> </Menu.Item>
</Menu> </Menu>
} }