refactor kanban
This commit is contained in:
parent
959059cfc7
commit
3d5c81618f
@ -350,7 +350,9 @@ function generateCardItemSchema(component) {
|
|||||||
},
|
},
|
||||||
Kanban: {
|
Kanban: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
name: 'kanban1',
|
'x-component': 'Kanban',
|
||||||
|
'x-designable-bar': 'Kanban.DesignableBar',
|
||||||
|
'x-component-props': {},
|
||||||
'x-decorator': 'CardItem',
|
'x-decorator': 'CardItem',
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
style: {
|
style: {
|
||||||
@ -360,60 +362,11 @@ function generateCardItemSchema(component) {
|
|||||||
padding: 0,
|
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: {
|
properties: {
|
||||||
create: {
|
create: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '添加卡片',
|
title: '添加卡片',
|
||||||
'x-designable-bar': 'Kanban.AddCardDesignableBar',
|
// 'x-designable-bar': 'Kanban.AddCardDesignableBar',
|
||||||
'x-component': 'Kanban.Card.AddNew',
|
'x-component': 'Kanban.Card.AddNew',
|
||||||
// 'x-decorator': 'AddNew.Displayed',
|
// 'x-decorator': 'AddNew.Displayed',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
@ -446,24 +399,12 @@ function generateCardItemSchema(component) {
|
|||||||
name: uid(),
|
name: uid(),
|
||||||
'x-decorator': 'Form',
|
'x-decorator': 'Form',
|
||||||
'x-component': 'Kanban.Card',
|
'x-component': 'Kanban.Card',
|
||||||
|
'x-designable-bar': 'Kanban.Card.DesignableBar',
|
||||||
'x-read-pretty': true,
|
'x-read-pretty': true,
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
useResource: '{{ Kanban.useResource }}',
|
useResource: '{{ Kanban.useSingleResource }}',
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
type: 'void',
|
|
||||||
'x-decorator': 'BlockItem',
|
|
||||||
'x-decorator-props': {
|
|
||||||
draggable: false,
|
|
||||||
},
|
|
||||||
'x-component': 'Grid',
|
|
||||||
'x-designable-bar': 'Kanban.Card.DesignableBar',
|
|
||||||
// 'x-component-props': {
|
|
||||||
// addNewComponent: 'AddNew.FormItem',
|
|
||||||
// },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
properties: {},
|
||||||
},
|
},
|
||||||
view1: {
|
view1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
@ -474,7 +415,7 @@ function generateCardItemSchema(component) {
|
|||||||
useOkAction: '{{ Kanban.useUpdateAction }}',
|
useOkAction: '{{ Kanban.useUpdateAction }}',
|
||||||
},
|
},
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
useResource: '{{ Kanban.useResource }}',
|
useResource: '{{ Kanban.useSingleResource }}',
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
[uid()]: {
|
||||||
@ -1286,8 +1227,9 @@ AddNew.CardItem = observer((props: any) => {
|
|||||||
data['x-component-props'][
|
data['x-component-props'][
|
||||||
'collectionName'
|
'collectionName'
|
||||||
] = collectionName;
|
] = collectionName;
|
||||||
data['x-component-props']['groupName'] =
|
data['x-component-props']['groupField'] = {
|
||||||
field.name;
|
name: field.name,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (isGridBlock(schema)) {
|
if (isGridBlock(schema)) {
|
||||||
path.pop();
|
path.pop();
|
||||||
|
@ -87,26 +87,14 @@ export const CardDesignableBar = observer((props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let data: ISchema = {
|
let data: ISchema = {
|
||||||
|
key: uid(),
|
||||||
|
name: uid(),
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Grid.Row',
|
'x-decorator': 'Form.Field.Item',
|
||||||
properties: {
|
// 'x-designable-bar': 'Form.Field.DesignableBar',
|
||||||
[uid()]: {
|
'x-component': 'Form.Field',
|
||||||
type: 'void',
|
'x-component-props': {
|
||||||
'x-component': 'Grid.Col',
|
fieldName: field.name,
|
||||||
properties: {
|
|
||||||
[uid()]: {
|
|
||||||
key: uid(),
|
|
||||||
type: 'void',
|
|
||||||
'x-decorator': 'Form.Field.Item',
|
|
||||||
'x-designable-bar':
|
|
||||||
'Form.Field.DesignableBar',
|
|
||||||
'x-component': 'Form.Field',
|
|
||||||
'x-component-props': {
|
|
||||||
fieldName: field.name,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const s = appendChild(data);
|
const s = appendChild(data);
|
||||||
|
@ -1,22 +1,61 @@
|
|||||||
import { ISchema } from '@formily/react';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SchemaRenderer } from '../../';
|
import { ISchema, SchemaRenderer } from '../../';
|
||||||
import { Kanban } from '..';
|
import { Kanban } from '..';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
|
||||||
const schema: ISchema = {
|
const schema: ISchema = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
name: 'kanban1',
|
name: 'kanban1',
|
||||||
'x-component': 'Kanban',
|
'x-component': 'Kanban',
|
||||||
|
'x-component-props': {
|
||||||
|
groupField: {
|
||||||
|
name: 'type',
|
||||||
|
enum: [
|
||||||
|
{
|
||||||
|
label: 'A',
|
||||||
|
value: 'A',
|
||||||
|
color: 'magenta',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'B',
|
||||||
|
value: 'B',
|
||||||
|
color: 'green',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'C',
|
||||||
|
value: 'C',
|
||||||
|
color: 'blue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'D',
|
||||||
|
value: 'D',
|
||||||
|
color: 'purple',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
default: [
|
default: [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
type: 'A',
|
type: 'A',
|
||||||
title: 'A1',
|
title: 'A1',
|
||||||
|
content: (
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, no dolor graeco pro, te sea bonorum
|
||||||
|
dolorum theophrastus.{' '}
|
||||||
|
</p>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'A',
|
type: 'A',
|
||||||
title: 'A2',
|
title: 'A2',
|
||||||
|
content: (
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, no dolor graeco pro, te sea bonorum
|
||||||
|
dolorum theophrastus.{' '}
|
||||||
|
</p>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
@ -27,11 +66,35 @@ const schema: ISchema = {
|
|||||||
id: '4',
|
id: '4',
|
||||||
type: 'B',
|
type: 'B',
|
||||||
title: 'B4',
|
title: 'B4',
|
||||||
|
content: (
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, no dolor graeco pro, te sea bonorum
|
||||||
|
dolorum theophrastus. Vim ea utamur appetere molestiae, ad harum
|
||||||
|
alienum indoctum ius. No quo laoreet laboramus comprehensam, eos paulo
|
||||||
|
integre vivendo eu, an nam alia facilisi consetetur. Pro exerci iisque
|
||||||
|
et, no amet magna iracundia vim. Vis erant consectetuer te, mei
|
||||||
|
menandri liberavisse at, no latine consulatu deseruisse eos. Mel an
|
||||||
|
novum nostrud scripserit, velit virtute delicata eam ad, eum ne etiam
|
||||||
|
omnesque.
|
||||||
|
</p>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: '5',
|
||||||
type: 'B',
|
type: 'B',
|
||||||
title: 'B5',
|
title: 'B5',
|
||||||
|
content: (
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, no dolor graeco pro, te sea bonorum
|
||||||
|
dolorum theophrastus. Vim ea utamur appetere molestiae, ad harum
|
||||||
|
alienum indoctum ius. No quo laoreet laboramus comprehensam, eos paulo
|
||||||
|
integre vivendo eu, an nam alia facilisi consetetur. Pro exerci iisque
|
||||||
|
et, no amet magna iracundia vim. Vis erant consectetuer te, mei
|
||||||
|
menandri liberavisse at, no latine consulatu deseruisse eos. Mel an
|
||||||
|
novum nostrud scripserit, velit virtute delicata eam ad, eum ne etiam
|
||||||
|
omnesque.
|
||||||
|
</p>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '6',
|
id: '6',
|
||||||
@ -42,6 +105,12 @@ const schema: ISchema = {
|
|||||||
id: '7',
|
id: '7',
|
||||||
type: 'C',
|
type: 'C',
|
||||||
title: 'C7',
|
title: 'C7',
|
||||||
|
content: (
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, no dolor graeco pro, te sea bonorum
|
||||||
|
dolorum theophrastus.{' '}
|
||||||
|
</p>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '8',
|
id: '8',
|
||||||
@ -55,40 +124,79 @@ const schema: ISchema = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
properties: {
|
properties: {
|
||||||
card1: {
|
create: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Kanban.Card',
|
title: '添加卡片',
|
||||||
|
// 'x-designable-bar': 'Kanban.AddCardDesignableBar',
|
||||||
|
'x-component': 'Kanban.Card.AddNew',
|
||||||
|
// 'x-decorator': 'AddNew.Displayed',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'text',
|
||||||
|
icon: 'PlusOutlined',
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
item1: {
|
modal: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Kanban.Item',
|
title: '新增数据',
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
'x-component': 'Action.Drawer',
|
||||||
|
'x-component-props': {
|
||||||
|
useOkAction: '{{ Kanban.useCreateAction }}',
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
title: {
|
[uid()]: {
|
||||||
type: 'string',
|
type: 'void',
|
||||||
// title: '标题',
|
'x-component': 'Grid',
|
||||||
'x-read-pretty': true,
|
'x-component-props': {
|
||||||
'x-decorator': 'FormItem',
|
addNewComponent: 'AddNew.FormItem',
|
||||||
'x-component': 'Input',
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
card1: {
|
||||||
|
type: 'void',
|
||||||
|
name: uid(),
|
||||||
|
'x-decorator': 'Form',
|
||||||
|
'x-component': 'Kanban.Card',
|
||||||
|
'x-designable-bar': 'Kanban.Card.DesignableBar',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
'x-decorator-props': {
|
||||||
|
useResource: '{{ Kanban.useSingleResource }}',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
// [uid()]: {
|
||||||
|
// type: 'void',
|
||||||
|
// 'x-decorator': 'BlockItem',
|
||||||
|
// 'x-decorator-props': {
|
||||||
|
// draggable: false,
|
||||||
|
// },
|
||||||
|
// 'x-component': 'Grid',
|
||||||
|
// 'x-designable-bar': 'Kanban.Card.DesignableBar',
|
||||||
|
// // 'x-component-props': {
|
||||||
|
// // addNewComponent: 'AddNew.FormItem',
|
||||||
|
// // },
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
},
|
||||||
view1: {
|
view1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
title: '修改数据',
|
||||||
|
'x-decorator': 'Form',
|
||||||
'x-component': 'Kanban.Card.View',
|
'x-component': 'Kanban.Card.View',
|
||||||
|
'x-component-props': {
|
||||||
|
useOkAction: '{{ Kanban.useUpdateAction }}',
|
||||||
|
},
|
||||||
|
'x-decorator-props': {
|
||||||
|
useResource: '{{ Kanban.useSingleResource }}',
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
item1: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Kanban.Item',
|
'x-component': 'Grid',
|
||||||
properties: {
|
'x-component-props': {
|
||||||
title: {
|
addNewComponent: 'AddNew.FormItem',
|
||||||
type: 'string',
|
|
||||||
title: '标题',
|
|
||||||
'x-read-pretty': true,
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -97,5 +205,5 @@ const schema: ISchema = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return <SchemaRenderer components={{ Kanban }} schema={schema} />;
|
return <SchemaRenderer scope={{ Kanban }} components={{ Kanban }} schema={schema} />;
|
||||||
};
|
};
|
||||||
|
@ -1,32 +1 @@
|
|||||||
---
|
<code src="./demos/demo1.tsx"/>
|
||||||
title: Kanban - 看板
|
|
||||||
nav:
|
|
||||||
title: 组件
|
|
||||||
path: /client
|
|
||||||
group:
|
|
||||||
order: 1
|
|
||||||
title: Schemas
|
|
||||||
path: /client/schemas
|
|
||||||
---
|
|
||||||
|
|
||||||
# Kanban - 看板
|
|
||||||
|
|
||||||
## Node Tree
|
|
||||||
|
|
||||||
<pre lang="tsx">
|
|
||||||
<Kanban>
|
|
||||||
<Kanban.Card>
|
|
||||||
// 看板卡片
|
|
||||||
<Kanban.Item>
|
|
||||||
<Input/>
|
|
||||||
</Kanban.Item>
|
|
||||||
</Kanban.Card>
|
|
||||||
<Kanban.Card.View>
|
|
||||||
// 查看卡片详情
|
|
||||||
</Kanban.Card.View>
|
|
||||||
</Kanban>
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
<code src="./demos/demo1.tsx" />
|
|
@ -1,4 +1,17 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import {
|
||||||
|
DndContext,
|
||||||
|
DragOverlay,
|
||||||
|
PointerSensor,
|
||||||
|
useDroppable,
|
||||||
|
useSensor,
|
||||||
|
useSensors,
|
||||||
|
} from '@dnd-kit/core';
|
||||||
|
import {
|
||||||
|
SortableContext,
|
||||||
|
useSortable,
|
||||||
|
verticalListSortingStrategy,
|
||||||
|
} from '@dnd-kit/sortable';
|
||||||
|
import { ArrayField } from '@formily/core';
|
||||||
import {
|
import {
|
||||||
observer,
|
observer,
|
||||||
RecursionField,
|
RecursionField,
|
||||||
@ -6,164 +19,147 @@ import {
|
|||||||
useField,
|
useField,
|
||||||
useForm,
|
useForm,
|
||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import {
|
import { Card, Spin, Tag } from 'antd';
|
||||||
DndContext,
|
|
||||||
DragOverlay,
|
|
||||||
closestCorners,
|
|
||||||
rectIntersection,
|
|
||||||
closestCenter,
|
|
||||||
useSensors,
|
|
||||||
useSensor,
|
|
||||||
MouseSensor,
|
|
||||||
} from '@dnd-kit/core';
|
|
||||||
import {
|
|
||||||
SortableContext,
|
|
||||||
rectSortingStrategy,
|
|
||||||
verticalListSortingStrategy,
|
|
||||||
horizontalListSortingStrategy,
|
|
||||||
} from '@dnd-kit/sortable';
|
|
||||||
import { useSortable, arrayMove } from '@dnd-kit/sortable';
|
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
|
||||||
import './style.less';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { groupBy } from 'lodash';
|
import { groupBy } from 'lodash';
|
||||||
|
import React, { createContext, useContext, useRef } from 'react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import { Button, Card, Drawer, Tag } from 'antd';
|
import './style.less';
|
||||||
import {
|
|
||||||
SchemaRenderer,
|
|
||||||
useDesignable,
|
|
||||||
} from '../../components/schema-renderer';
|
|
||||||
import { createContext } from 'react';
|
|
||||||
import { useContext } from 'react';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { DesignableBar } from './DesignableBar';
|
|
||||||
import { CollectionProvider, useCollectionContext } from '../../constate';
|
import { CollectionProvider, useCollectionContext } from '../../constate';
|
||||||
import { Resource } from '../../resource';
|
import { Resource } from '../../resource';
|
||||||
import { useRequest } from 'ahooks';
|
import { useRequest } from 'ahooks';
|
||||||
import { Action } from '../action';
|
import { Action } from '../action';
|
||||||
import { Form } from '../form';
|
|
||||||
import { CardDesignableBar } from './CardDesignableBar';
|
import { CardDesignableBar } from './CardDesignableBar';
|
||||||
import { VisibleContext } from '../../context';
|
import { VisibleContext } from '../../context';
|
||||||
|
import { DesignableBar } from './DesignableBar';
|
||||||
|
import { useDesignable } from '../../components/schema-renderer';
|
||||||
|
|
||||||
export function SortableItem(props) {
|
function Droppable(props) {
|
||||||
|
const { id, data, ...others } = props;
|
||||||
|
const { setNodeRef } = useDroppable({
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
|
return <div ref={setNodeRef} {...others}></div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SortableItem(props) {
|
||||||
|
const { id, data, className, ...others } = props;
|
||||||
const nodeRef = useRef<any>();
|
const nodeRef = useRef<any>();
|
||||||
const {
|
const {
|
||||||
|
isDragging,
|
||||||
attributes,
|
attributes,
|
||||||
listeners,
|
listeners,
|
||||||
setNodeRef,
|
setNodeRef,
|
||||||
isDragging,
|
|
||||||
transform,
|
transform,
|
||||||
transition,
|
transition,
|
||||||
} = useSortable({
|
} = useSortable({
|
||||||
id: props.id,
|
id,
|
||||||
disabled: props.disabled,
|
data: { ...data, nodeRef },
|
||||||
data: {
|
|
||||||
type: props.type,
|
|
||||||
nodeRef,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
|
...props.style,
|
||||||
transform: CSS.Transform.toString(transform),
|
transform: CSS.Transform.toString(transform),
|
||||||
transition,
|
transition,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cls({ isDragging }, props.className)}
|
{...others}
|
||||||
ref={(el) => {
|
ref={(el) => {
|
||||||
setNodeRef(el);
|
setNodeRef(el);
|
||||||
nodeRef.current = el;
|
nodeRef.current = el;
|
||||||
}}
|
}}
|
||||||
style={style}
|
style={style}
|
||||||
|
className={cls(className, { isDragging })}
|
||||||
{...attributes}
|
{...attributes}
|
||||||
{...listeners}
|
{...listeners}
|
||||||
>
|
>
|
||||||
{/* <div {...listeners}>drag</div> */}
|
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useColumns = () => {
|
interface KanbanContextProps {
|
||||||
const field = useField<Formily.Core.Models.ArrayField>();
|
props?: any;
|
||||||
const { getField } = useCollectionContext();
|
service?: any;
|
||||||
let groupField = field.componentProps.groupField;
|
resource?: Resource;
|
||||||
const groupName = field.componentProps.groupName;
|
[key: string]: any;
|
||||||
const collectionField = getField(groupName);
|
|
||||||
if (collectionField) {
|
|
||||||
groupField = collectionField?.uiSchema;
|
|
||||||
}
|
|
||||||
const values = field.value;
|
|
||||||
const columns = groupField?.enum?.map((group) => {
|
|
||||||
return {
|
|
||||||
...group,
|
|
||||||
items: values?.filter((item) => item[groupName] === group.value) || [],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
console.log('useColumns', { values, columns });
|
|
||||||
return { values, columns };
|
|
||||||
};
|
|
||||||
|
|
||||||
interface KanbanCardContextProps {
|
|
||||||
index: number;
|
|
||||||
record?: any;
|
|
||||||
viewSchema: Schema;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const KanbanCardContext = createContext<KanbanCardContextProps>(null);
|
interface KanbanCardContextProps {
|
||||||
|
record?: any;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
export const KanbanContext = createContext<any>(null);
|
interface KanbanColumnContextProps {
|
||||||
export const KanbanColumnContext = createContext<any>(null);
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const KanbanContext = createContext<KanbanContextProps>(null);
|
||||||
|
export const KanbanColumnContext =
|
||||||
|
createContext<KanbanColumnContextProps>(null);
|
||||||
|
export const KanbanCardContext = createContext<KanbanCardContextProps>(null);
|
||||||
|
|
||||||
export const useKanban = () => {
|
export const useKanban = () => {
|
||||||
return useContext(KanbanContext);
|
return useContext(KanbanContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MyMouseSensor extends MouseSensor {
|
const KanbanColumn = (props) => {
|
||||||
static activators = [
|
const option = useContext(KanbanColumnContext);
|
||||||
{
|
const { field, schemas } = useKanban();
|
||||||
eventName: 'onMouseDown' as const,
|
const { items } = props;
|
||||||
handler: (event) => {
|
return (
|
||||||
if (event.target.tagName === 'DIV') {
|
<SortableContext
|
||||||
return true;
|
// id={option.value}
|
||||||
}
|
items={items || []}
|
||||||
console.log('event.target.tagName', event.target.tagName);
|
strategy={verticalListSortingStrategy}
|
||||||
event.target;
|
>
|
||||||
return false;
|
{items?.map((item) => {
|
||||||
},
|
const index = field.value?.findIndex((val) => val.id === item.id);
|
||||||
},
|
return (
|
||||||
];
|
<SortableItem
|
||||||
constructor(props) {
|
className={'nb-kanban-item'}
|
||||||
super(props);
|
key={item.id}
|
||||||
}
|
id={item.id}
|
||||||
}
|
data={{ type: 'card', columnId: option.value }}
|
||||||
|
>
|
||||||
|
<KanbanCardContext.Provider
|
||||||
|
value={{ index, schemas, record: item }}
|
||||||
|
>
|
||||||
|
{/* <Card bordered={false}>{item.id}</Card> */}
|
||||||
|
<RecursionField
|
||||||
|
name={index}
|
||||||
|
schema={schemas.get('Kanban.Card')}
|
||||||
|
/>
|
||||||
|
</KanbanCardContext.Provider>
|
||||||
|
</SortableItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SortableContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const InternalKanban = observer((props: any) => {
|
const InternalKanban = observer((props: any) => {
|
||||||
const field = useField<Formily.Core.Models.ArrayField>();
|
const { getField } = useCollectionContext();
|
||||||
const groupName = field.componentProps.groupName;
|
const collectionField = getField(props.groupField?.name);
|
||||||
const { values, columns } = useColumns();
|
const groupField = {
|
||||||
const { schema } = useDesignable();
|
...collectionField?.uiSchema,
|
||||||
|
...props.groupField,
|
||||||
|
};
|
||||||
|
const field = useField<ArrayField>();
|
||||||
|
const sensors = useSensors(
|
||||||
|
useSensor(PointerSensor, {
|
||||||
|
activationConstraint: {
|
||||||
|
distance: 10,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const groups = groupBy(field.value, groupField.name);
|
||||||
const { collectionName } = props;
|
const { collectionName } = props;
|
||||||
const cardSchema = schema.reduceProperties((prev, current) => {
|
|
||||||
if (current['x-component'] === 'Kanban.Card') {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
return prev;
|
|
||||||
}, null);
|
|
||||||
const cardViewSchema = schema.reduceProperties((prev, current) => {
|
|
||||||
if (current['x-component'] === 'Kanban.Card.View') {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
return prev;
|
|
||||||
}, null);
|
|
||||||
const addCardSchema = schema.reduceProperties((prev, current) => {
|
|
||||||
if (current['x-component'] === 'Kanban.Card.AddNew') {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
return prev;
|
|
||||||
}, null);
|
|
||||||
const resource = Resource.make(collectionName);
|
const resource = Resource.make(collectionName);
|
||||||
const service = useRequest(
|
const service = useRequest(
|
||||||
(params) => {
|
(params) => {
|
||||||
@ -180,163 +176,153 @@ const InternalKanban = observer((props: any) => {
|
|||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
field.setValue(data);
|
field.setValue(data);
|
||||||
},
|
},
|
||||||
manual: true,
|
// manual: true,
|
||||||
// refreshDeps: [props.fieldNames],
|
// refreshDeps: [props.fieldNames],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
const { schema } = useDesignable();
|
||||||
service.run({
|
const [schemas, setSchemas] = useState(() => {
|
||||||
defaultFilter: props?.defaultFilter,
|
const schemas = new Map<string, Schema>();
|
||||||
sort: 'sort',
|
schema.reduceProperties((map, current) => {
|
||||||
});
|
if (current['x-component'] === 'Kanban.Card') {
|
||||||
}, [props.defaultFilter]);
|
map.set('Kanban.Card', current);
|
||||||
const [dragOverlayContent, setDragOverlayContent] = useState('');
|
}
|
||||||
const [style, setStyle] = useState({});
|
if (current['x-component'] === 'Kanban.Card.AddNew') {
|
||||||
const containerRef = useRef<HTMLDivElement>();
|
map.set('Kanban.Card.AddNew', current);
|
||||||
console.log({ style });
|
}
|
||||||
const sensors = useSensors(useSensor(MyMouseSensor));
|
if (current['x-component'] === 'Kanban.Card.View') {
|
||||||
|
map.set('Kanban.Card.View', current);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, schemas);
|
||||||
|
return schemas;
|
||||||
|
});
|
||||||
|
const addNewCardSchema = schemas.get('Kanban.Card.AddNew');
|
||||||
|
console.log('field.value', schemas);
|
||||||
return (
|
return (
|
||||||
<KanbanContext.Provider value={{ field, resource, service, props }}>
|
<KanbanContext.Provider
|
||||||
<div>
|
value={{ field, resource, service, schemas, props }}
|
||||||
{/* <div>
|
>
|
||||||
<Button>筛选</Button>
|
<DndContext
|
||||||
</div> */}
|
sensors={sensors}
|
||||||
<div className={'kanban'}>
|
onDragMove={({ active, over }) => {
|
||||||
<div ref={containerRef} className={'kanban-container'}>
|
const overId = over?.id;
|
||||||
<DndContext
|
const activeId = active?.id;
|
||||||
sensors={sensors}
|
if (!overId || !activeId) {
|
||||||
onDragStart={(event) => {
|
return;
|
||||||
const el = event?.active?.data?.current?.nodeRef
|
}
|
||||||
?.current as HTMLElement;
|
if (overId === activeId) {
|
||||||
console.log(event, el);
|
return;
|
||||||
setDragOverlayContent(el?.outerHTML);
|
}
|
||||||
setStyle({
|
const overType = over?.data?.current?.type;
|
||||||
width: el.clientWidth,
|
const activeItem = field.value.find((item) => item.id === activeId);
|
||||||
height: containerRef.current?.clientHeight,
|
if (overType === 'column') {
|
||||||
});
|
if (overId === activeItem?.[groupField.name]) {
|
||||||
}}
|
return;
|
||||||
collisionDetection={closestCenter}
|
}
|
||||||
onDragEnd={({ active, over }) => {
|
const len = groups?.[overId]?.length;
|
||||||
const source = values.find((item) => item.id === active?.id);
|
if (len > 0) {
|
||||||
const target = values.find((item) => item.id === over?.id);
|
const last = groups?.[overId]?.[len - 1];
|
||||||
if (source && target) {
|
const activeIndex = field.value.findIndex(
|
||||||
const fromIndex = values.findIndex(
|
(item) => item.id === activeId,
|
||||||
(item) => item.id === active?.id,
|
);
|
||||||
);
|
const overIndex = field.value.findIndex(
|
||||||
const toIndex = values.findIndex(
|
(item) => item.id === last.id,
|
||||||
(item) => item.id === over?.id,
|
);
|
||||||
);
|
console.log({ overId, last, overIndex, activeIndex });
|
||||||
// setValues((items) => {
|
field.move(activeIndex, overIndex);
|
||||||
// const fromIndex = items.findIndex(
|
}
|
||||||
// (item) => item.id === active?.id,
|
activeItem[groupField.name] = overId;
|
||||||
// );
|
} else {
|
||||||
// const toIndex = items.findIndex(
|
const overColumnId = over?.data?.current?.columnId;
|
||||||
// (item) => item.id === over?.id,
|
const activeColumnId = active?.data?.current?.columnId;
|
||||||
// );
|
if (!overColumnId || !activeColumnId) {
|
||||||
// return arrayMove(items, fromIndex, toIndex);
|
return;
|
||||||
// });
|
}
|
||||||
field.move(fromIndex, toIndex);
|
if (overColumnId !== activeColumnId) {
|
||||||
}
|
activeItem[groupField.name] = overColumnId;
|
||||||
}}
|
const activeIndex = field.value.findIndex(
|
||||||
onDragOver={({ active, over }) => {
|
(item) => item.id === activeId,
|
||||||
if (!over) {
|
);
|
||||||
return;
|
const overIndex = field.value.findIndex(
|
||||||
}
|
(item) => item.id === overId,
|
||||||
if (active?.id === over?.id) {
|
);
|
||||||
return;
|
console.log({ overId, overIndex, activeIndex });
|
||||||
}
|
field.move(activeIndex, overIndex);
|
||||||
const source = values.find((item) => item.id === active?.id);
|
}
|
||||||
if (source && over?.data?.current?.type === 'column') {
|
}
|
||||||
source[groupName] = over.id;
|
}}
|
||||||
return;
|
onDragEnd={({ active, over }) => {
|
||||||
}
|
const overId = over?.id;
|
||||||
console.log('active.id', active.id, over?.data?.current);
|
const activeId = active?.id;
|
||||||
const target = values.find((item) => item.id === over?.id);
|
if (!overId || !activeId) {
|
||||||
if (source && target) {
|
return;
|
||||||
if (source[groupName] !== target[groupName]) {
|
}
|
||||||
source[groupName] = target[groupName];
|
if (overId === activeId) {
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}}
|
const overType = over?.data?.current?.type;
|
||||||
>
|
if (overType !== 'column') {
|
||||||
<DragOverlay
|
const overColumnId = over?.data?.current?.columnId;
|
||||||
style={{ ...style }}
|
const activeColumnId = active?.data?.current?.columnId;
|
||||||
// style={{ pointerEvents: 'none' }}
|
if (!overColumnId || !activeColumnId) {
|
||||||
>
|
return;
|
||||||
<div
|
}
|
||||||
className={'nb-kanban-drag-overlay'}
|
if (overColumnId !== activeColumnId) {
|
||||||
style={{
|
return;
|
||||||
...style,
|
}
|
||||||
}}
|
const activeIndex = field.value.findIndex(
|
||||||
dangerouslySetInnerHTML={{ __html: dragOverlayContent }}
|
(item) => item.id === activeId,
|
||||||
/>
|
);
|
||||||
</DragOverlay>
|
const overIndex = field.value.findIndex(
|
||||||
|
(item) => item.id === overId,
|
||||||
<SortableContext
|
);
|
||||||
strategy={horizontalListSortingStrategy}
|
field.move(activeIndex, overIndex);
|
||||||
items={
|
}
|
||||||
columns?.map((column) => ({
|
}}
|
||||||
...column,
|
>
|
||||||
id: column.value,
|
<DragOverlay style={{ background: 'yellow' }}>
|
||||||
})) || []
|
<div>aaa</div>
|
||||||
}
|
</DragOverlay>
|
||||||
>
|
<Spin spinning={service.loading}>
|
||||||
{columns?.map((column) => (
|
<div
|
||||||
<SortableItem
|
className={'nb-kanban-board'}
|
||||||
id={column.value}
|
style={{
|
||||||
key={column.value}
|
// display: 'flex',
|
||||||
type={'column'}
|
// flexDirection: 'row',
|
||||||
disabled
|
userSelect: 'none',
|
||||||
className={'column'}
|
}}
|
||||||
|
>
|
||||||
|
<div className={'nb-kanban-container'}>
|
||||||
|
{groupField?.enum?.map((option) => {
|
||||||
|
const items = field.value?.filter(
|
||||||
|
(item) => item?.[groupField.name] === option.value,
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Droppable
|
||||||
|
id={option.value}
|
||||||
|
data={{
|
||||||
|
type: 'column',
|
||||||
|
}}
|
||||||
|
className={'nb-kanban-column'}
|
||||||
>
|
>
|
||||||
<KanbanColumnContext.Provider value={column}>
|
<KanbanColumnContext.Provider value={option}>
|
||||||
<div className={'column-title'}>
|
<div className={'nb-kanban-column-header'}>
|
||||||
<Tag color={column.color}>{column.label}</Tag>
|
<Tag color={option.color}>{option.label}</Tag>
|
||||||
</div>
|
</div>
|
||||||
<SortableContext
|
<KanbanColumn items={items} />
|
||||||
strategy={verticalListSortingStrategy}
|
|
||||||
items={column.items}
|
|
||||||
>
|
|
||||||
{column.items.map((item) => {
|
|
||||||
const index = values.findIndex(
|
|
||||||
(value) => value.id === item.id,
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<SortableItem
|
|
||||||
key={item.id}
|
|
||||||
className={'item'}
|
|
||||||
id={item.id}
|
|
||||||
// disabled
|
|
||||||
type={'item'}
|
|
||||||
>
|
|
||||||
<KanbanCardContext.Provider
|
|
||||||
value={{
|
|
||||||
index: index,
|
|
||||||
record: item,
|
|
||||||
viewSchema: cardViewSchema,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RecursionField
|
|
||||||
name={index}
|
|
||||||
schema={cardSchema}
|
|
||||||
/>
|
|
||||||
</KanbanCardContext.Provider>
|
|
||||||
</SortableItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</SortableContext>
|
|
||||||
<RecursionField
|
<RecursionField
|
||||||
name={addCardSchema?.name}
|
name={addNewCardSchema.name}
|
||||||
schema={addCardSchema}
|
schema={addNewCardSchema}
|
||||||
/>
|
/>
|
||||||
</KanbanColumnContext.Provider>
|
</KanbanColumnContext.Provider>
|
||||||
</SortableItem>
|
</Droppable>
|
||||||
))}
|
);
|
||||||
</SortableContext>
|
})}
|
||||||
</DndContext>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Spin>
|
||||||
</div>
|
</DndContext>
|
||||||
</KanbanContext.Provider>
|
</KanbanContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -352,13 +338,13 @@ export const Kanban: any = observer((props: any) => {
|
|||||||
Kanban.useCreateAction = () => {
|
Kanban.useCreateAction = () => {
|
||||||
const { service, resource, props } = useKanban();
|
const { service, resource, props } = useKanban();
|
||||||
const column = useContext(KanbanColumnContext);
|
const column = useContext(KanbanColumnContext);
|
||||||
const groupName = props.groupName;
|
const groupField = props.groupField;
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
return {
|
return {
|
||||||
async run() {
|
async run() {
|
||||||
await resource.create({
|
await resource.create({
|
||||||
...form.values,
|
...form.values,
|
||||||
[groupName]: column.value,
|
[groupField.name]: column.value,
|
||||||
});
|
});
|
||||||
await form.reset();
|
await form.reset();
|
||||||
return service.refresh();
|
return service.refresh();
|
||||||
@ -380,7 +366,7 @@ Kanban.useUpdateAction = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Kanban.useResource = ({ onSuccess }) => {
|
Kanban.useSingleResource = ({ onSuccess }) => {
|
||||||
const { props } = useKanban();
|
const { props } = useKanban();
|
||||||
const { collection } = useCollectionContext();
|
const { collection } = useCollectionContext();
|
||||||
const ctx = useContext(KanbanCardContext);
|
const ctx = useContext(KanbanCardContext);
|
||||||
@ -408,7 +394,7 @@ Kanban.useResource = ({ onSuccess }) => {
|
|||||||
|
|
||||||
Kanban.Card = observer((props) => {
|
Kanban.Card = observer((props) => {
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const { index, viewSchema } = useContext(KanbanCardContext);
|
const { index, schemas } = useContext(KanbanCardContext);
|
||||||
const { DesignableBar } = useDesignable();
|
const { DesignableBar } = useDesignable();
|
||||||
const { children, ...others } = props;
|
const { children, ...others } = props;
|
||||||
return (
|
return (
|
||||||
@ -421,9 +407,9 @@ Kanban.Card = observer((props) => {
|
|||||||
{...others}
|
{...others}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<DesignableBar />
|
|
||||||
</Card>
|
</Card>
|
||||||
<RecursionField name={index} schema={viewSchema} />
|
<DesignableBar />
|
||||||
|
<RecursionField name={index} schema={schemas.get('Kanban.Card.View')} />
|
||||||
</VisibleContext.Provider>
|
</VisibleContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,80 +1,31 @@
|
|||||||
.column {
|
.nb-kanban-board {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.nb-kanban-container {
|
||||||
|
display: flex;
|
||||||
|
min-height: 60vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nb-kanban-column {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
background: #f9f9f9;
|
background: #f9f9f9;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
&.isDragging {
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
&.add-column {
|
|
||||||
padding: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
background: rgba(241, 139, 98, 0.05);
|
|
||||||
cursor: pointer;
|
|
||||||
color: #f18b62;
|
|
||||||
text-align: center;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
.add-column-plus {
|
|
||||||
position: relative;
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px;
|
|
||||||
height: 50px;
|
|
||||||
background: #f18b62;
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
width: 50px;
|
|
||||||
height: 1px;
|
|
||||||
background: #f18b62;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
background: rgba(241, 139, 98, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nb-kanban-drag-overlay {
|
.nb-kanban-item {
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column-title {
|
|
||||||
line-height: 24px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
&.isDragging {
|
||||||
|
opacity: .2;
|
||||||
.kanban {
|
|
||||||
display: flex;
|
|
||||||
margin: 0;
|
|
||||||
overflow: auto;
|
|
||||||
.item {
|
|
||||||
position: relative;
|
|
||||||
// background: #f1f1f1;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
&.isDragging {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.kanban-container {
|
.nb-kanban-item {
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drag-handle {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanban .ant-card-body {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0;
|
// padding: 0;
|
||||||
|
|
||||||
> .nb-block-item {
|
> .nb-block-item {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
@ -117,3 +68,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nb-kanban-column-header {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user