refactor kanban

This commit is contained in:
chenos 2021-08-23 01:23:04 +08:00
parent 959059cfc7
commit 3d5c81618f
6 changed files with 419 additions and 470 deletions

View File

@ -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();

View File

@ -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);

View File

@ -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} />;
}; };

View File

@ -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" />

View File

@ -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>
); );
}); });

View File

@ -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;
}