feat: improve kanban

This commit is contained in:
chenos 2021-08-19 23:37:02 +08:00
parent dfd4b821ff
commit 5299a2ece9
11 changed files with 790 additions and 202 deletions

View File

@ -145,6 +145,7 @@ export const SchemaField = createSchemaField({
scope: {
Table,
Calendar,
Kanban,
useAsyncDataSource,
ChinaRegion: {
loadData: loadChinaRegionData,

View File

@ -5,7 +5,11 @@ const [CollectionsProvider, useCollectionsContext] = constate(() => {
const result = useRequest('collections:findAll', {
formatResult: (result) => result?.data,
});
return { ...result, collections: result.data || [] };
return {
...result, collections: result.data || [], findCollection(name) {
return result?.data?.find((item) => item.name === name);
}
};
});
export { CollectionsProvider, useCollectionsContext };

View File

@ -1,9 +1,13 @@
import { useEffect } from 'react';
import { useCollectionContext } from '../constate';
import { Resource } from '../resource';
export const useResource = (options: any = {}) => {
const { collection } = useCollectionContext();
const resource = Resource.make(collection?.name);
useEffect(() => {
options.onSuccess && options.onSuccess({});
}, []);
return {
initialValues: {},
loading: false,

View File

@ -410,40 +410,66 @@ function generateCardItemSchema(component) {
},
],
properties: {
card1: {
create: {
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: {
item1: {
modal: {
type: 'void',
'x-component': 'Kanban.Item',
title: '新增数据',
'x-decorator': 'Form',
'x-component': 'Action.Drawer',
'x-component-props': {
useOkAction: '{{ Kanban.useCreateAction }}',
},
properties: {
title: {
type: 'string',
// title: '标题',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
[uid()]: {
type: 'void',
'x-component': 'Grid',
'x-component-props': {
addNewComponent: 'AddNew.FormItem',
},
},
},
},
},
},
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.useResource }}',
},
properties: {},
},
view1: {
type: 'void',
title: '修改数据',
'x-decorator': 'Form',
'x-component': 'Kanban.Card.View',
'x-component-props': {
useOkAction: '{{ Kanban.useUpdateAction }}',
},
'x-decorator-props': {
useResource: '{{ Kanban.useResource }}',
},
properties: {
item1: {
[uid()]: {
type: 'void',
'x-component': 'Kanban.Item',
properties: {
title: {
type: 'string',
title: '标题',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
'x-component': 'Grid',
'x-component-props': {
addNewComponent: 'AddNew.FormItem',
},
},
},
@ -1225,46 +1251,58 @@ AddNew.CardItem = observer((props: any) => {
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>
{item?.generalFields
?.filter((item) => {
return item?.uiSchema?.enum;
})
?.map((field) => {
return (
<Menu.Item
style={{ minWidth: 150 }}
key={`Kanban.collection.${field.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']['groupName'] =
field.name;
}
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);
}
}}
>
{field?.uiSchema?.title}
</Menu.Item>
);
})}
</Menu.ItemGroup>
</Menu.SubMenu>
// <Menu.Item
@ -1553,7 +1591,7 @@ AddNew.PaneItem = observer((props: any) => {
const [visible, setVisible] = useState(false);
const blockSchema = useContext(BlockSchemaContext);
const useResource = `{{ ${blockSchema['x-component']}.useResource }}`;
console.log('AddNew.PaneItem.useResource', useResource)
console.log('AddNew.PaneItem.useResource', useResource);
return (
<Dropdown
trigger={['click']}

View File

@ -12,7 +12,7 @@ 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 { SettingOutlined } from '@ant-design/icons';
import { Droppable, SortableItem } from '../../components/Sortable';
export const ActionBar = observer((props: any) => {
@ -257,7 +257,7 @@ function AddActionButton() {
className={'designable-btn designable-btn-dash'}
style={{ marginLeft: 8 }}
type={'dashed'}
icon={<PlusOutlined />}
icon={<SettingOutlined />}
>
</Button>

View File

@ -183,7 +183,7 @@ export const Calendar: any = observer((props: any) => {
console.log('field.value', field.value);
return (
<CollectionProvider collectionName={props.collectionName}>
<CalendarContext.Provider value={{ resource, service, props }}>
<CalendarContext.Provider value={{ field, resource, service, props }}>
<div {...props} style={{ height: 700 }}>
<Drawer
width={'50%'}

View File

@ -28,6 +28,7 @@ import { uid } from '@formily/shared';
import { getSchemaPath } from '../../components/schema-renderer';
import { useCollection, useCollectionContext } from '../../constate';
import { useTable } from '../table';
import { BlockSchemaContext } from '../../context';
export const DesignableBar = observer((props) => {
const field = useField();
@ -35,12 +36,14 @@ export const DesignableBar = observer((props) => {
const [visible, setVisible] = useState(false);
const { dragRef } = useContext(DraggableBlockContext);
const { props: tableProps } = useTable();
const { collection } = useCollectionContext();
// const collectionName = field.componentProps?.collectionName || tableProps?.collectionName;
// const { collection } = useCollection({ collectionName });
const blockSchema = useContext(BlockSchemaContext);
const collectionName = blockSchema['x-component-props']?.collectionName;
const { collection } = useCollection({ collectionName });
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();

View File

@ -0,0 +1,115 @@
import React, { useContext, useMemo, useRef, useState } from 'react';
import { createForm } from '@formily/core';
import {
SchemaOptionsContext,
Schema,
useFieldSchema,
observer,
SchemaExpressionScopeContext,
FormProvider,
useField,
useForm,
RecursionField,
} from '@formily/react';
import {
useSchemaPath,
SchemaField,
useDesignable,
removeSchema,
ISchema,
createSchema,
} 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,
useDisplayedMapContext,
} from '../../constate';
import { useTable } from '../table';
import SwitchMenuItem from '../../components/SwitchMenuItem';
export const CardDesignableBar = observer((props) => {
const field = useField();
const { designable, schema, appendChild, deepRemove } = useDesignable();
const [visible, setVisible] = useState(false);
const { dragRef } = useContext(DraggableBlockContext);
const { collection, fields } = useCollectionContext();
const displayed = useDisplayedMapContext();
console.log('useDisplayedMapContext', schema);
return (
<div className={cls('designable-bar', { active: visible })}>
<span
onClick={(e) => {
e.stopPropagation();
}}
className={cls('designable-bar-actions', { active: visible })}
>
<Space size={2}>
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.ItemGroup className={'display-fields'} title={`字段展示`}>
{fields?.map((field) => (
<SwitchMenuItem
key={field.key}
title={field?.uiSchema?.title}
checked={displayed.has(field.name)}
onChange={async (checked) => {
if (!checked) {
const s: any = displayed.get(field.name);
const p = getSchemaPath(s);
const removed = deepRemove(p);
if (!removed) {
console.log('getSchemaPath', p, removed);
return;
}
const last = removed.pop();
displayed.remove(field.name);
if (isGridRowOrCol(last)) {
await removeSchema(last);
}
return;
}
let data: ISchema = {
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);
await createSchema(s);
}}
/>
))}
</Menu.ItemGroup>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);
});

View File

@ -0,0 +1,201 @@
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, Switch } 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 { RandomNameContext } from '.';
import { useCollectionContext, useDisplayedMapContext } from '../../constate';
import SwitchMenuItem from '../../components/SwitchMenuItem';
export const FieldDesignableBar = observer((props) => {
const field = useField();
const { schema, deepRemove } = useDesignable();
const [visible, setVisible] = useState(false);
const { dragRef } = useContext(DraggableBlockContext);
const randomName = useContext(RandomNameContext);
const displayed = useDisplayedMapContext();
const fieldName = schema['x-component-props']?.['fieldName'];
const { getField } = useCollectionContext();
const collectionField = getField(fieldName);
console.log({ collectionField });
const realField = field
.query(field.address.concat(randomName, fieldName))
.take();
return (
<div className={cls('designable-bar', { active: visible })}>
<span
onClick={(e) => {
e.stopPropagation();
}}
className={cls('designable-bar-actions', { active: visible })}
>
<Space size={2}>
<AddNew.FormItem defaultAction={'insertAfter'} ghost />
{dragRef && <DragOutlined ref={dragRef} />}
<Dropdown
placement={'bottomRight'}
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.Item
key={'update'}
onClick={async () => {
const values = await FormDialog('修改字段名称', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
fieldName: {
type: 'string',
title: '原字段名称',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
title: {
type: 'string',
title: '自定义名称',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
}}
/>
</FormLayout>
);
}).open({
initialValues: {
fieldName: collectionField?.uiSchema?.title,
title: schema['title'],
},
});
const title = values.title || null;
field
.query(field.address.concat(randomName, fieldName))
.take((f) => {
f.title = title || collectionField?.uiSchema?.title;
});
schema['title'] = title;
await updateSchema({
key: schema['key'],
title,
});
}}
>
</Menu.Item>
<Menu.Item
style={{ minWidth: 150 }}
onClick={async () => {
const values = await FormDialog('编辑描述', () => {
return (
<FormLayout layout={'vertical'}>
<SchemaField
schema={{
type: 'object',
properties: {
description: {
type: 'string',
'x-component': 'Input.TextArea',
},
},
}}
/>
</FormLayout>
);
}).open({
initialValues: {
description: schema['description'],
},
});
const description = values.description || null;
realField.description =
description || collectionField?.uiSchema?.description;
schema['description'] = description;
await updateSchema({
key: schema['key'],
description,
});
}}
>
</Menu.Item>
<SwitchMenuItem
title={'必填'}
checked={schema.required as boolean}
onChange={(checked) => {
field
.query(field.address.concat(randomName, fieldName))
.take((f: any) => {
f.required = checked;
schema.required = checked;
updateSchema(schema);
});
}}
/>
<Menu.Divider />
<Menu.Item
key={'delete'}
onClick={async () => {
const removed = deepRemove();
const fieldName =
schema['x-component-props']?.['fieldName'];
console.log({ schema, removed, fieldName });
const last = removed.pop();
displayed.remove(fieldName);
if (isGridRowOrCol(last)) {
await removeSchema(last);
}
}}
>
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
</Space>
</span>
</div>
);
});

View File

@ -1,5 +1,11 @@
import React from 'react';
import { observer, RecursionField, Schema, useField } from '@formily/react';
import React, { useEffect } from 'react';
import {
observer,
RecursionField,
Schema,
useField,
useForm,
} from '@formily/react';
import { DndContext, DragOverlay, closestCorners } from '@dnd-kit/core';
import {
SortableContext,
@ -13,7 +19,7 @@ import './style.less';
import { useState } from 'react';
import { groupBy } from 'lodash';
import cls from 'classnames';
import { Button, Card, Drawer } from 'antd';
import { Button, Card, Drawer, Tag } from 'antd';
import {
SchemaRenderer,
useDesignable,
@ -23,6 +29,13 @@ 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 { Resource } from '../../resource';
import { useRequest } from 'ahooks';
import { Action } from '../action';
import { Form } from '../form';
import { CardDesignableBar } from './CardDesignableBar';
import { VisibleContext } from '../../context';
export function SortableItem(props) {
const {
@ -59,16 +72,22 @@ export function SortableItem(props) {
const useColumns = () => {
const field = useField<Formily.Core.Models.ArrayField>();
const { getField } = useCollectionContext();
let groupField = field.componentProps.groupField;
const groupName = field.componentProps.groupName;
const collectionField = getField(groupName);
if (collectionField) {
groupField = collectionField?.uiSchema;
}
const values = field.value;
const [groups, setGroups] = useState(['A', 'B', 'C']);
const columns = groups.map((name) => {
const columns = groupField?.enum?.map((group) => {
return {
id: name,
title: name,
items: values?.filter((item) => item.type === name) || [],
...group,
items: values?.filter((item) => item[groupName] === group.value) || [],
};
});
return { values, groups, columns, setGroups };
console.log('useColumns', { values, columns });
return { values, columns };
};
interface KanbanCardContextProps {
@ -79,10 +98,19 @@ interface KanbanCardContextProps {
const KanbanCardContext = createContext<KanbanCardContextProps>(null);
export const Kanban: any = observer(() => {
export const KanbanContext = createContext<any>(null);
export const KanbanColumnContext = createContext<any>(null);
export const useKanban = () => {
return useContext(KanbanContext);
};
const InternalKanban = observer((props: any) => {
const field = useField<Formily.Core.Models.ArrayField>();
const groupName = field.componentProps.groupName;
const { values, columns } = useColumns();
const { schema } = useDesignable();
const { collectionName } = props;
const cardSchema = schema.reduceProperties((prev, current) => {
if (current['x-component'] === 'Kanban.Card') {
return current;
@ -95,148 +123,270 @@ export const Kanban: any = observer(() => {
}
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 service = useRequest(
(params) => {
if (!collectionName) {
return Promise.resolve([]);
}
return resource.list({
...params,
perPage: -1,
});
},
{
formatResult: (data) => data?.data,
onSuccess(data) {
field.setValue(data);
},
manual: true,
// refreshDeps: [props.fieldNames],
},
);
useEffect(() => {
service.run({
defaultFilter: props?.defaultFilter,
sort: 'sort',
});
}, [props.defaultFilter]);
return (
<div>
{/* <div>
<KanbanContext.Provider value={{ field, resource, service, props }}>
<div>
{/* <div>
<Button></Button>
</div> */}
<div className={'kanban'}>
<DndContext
// collisionDetection={closestCorners}
onDragEnd={({ active, over }) => {
const source = values.find((item) => item.id === active?.id);
const target = values.find((item) => item.id === over?.id);
if (source && target) {
const fromIndex = values.findIndex(
(item) => item.id === active?.id,
);
const toIndex = values.findIndex((item) => item.id === over?.id);
// setValues((items) => {
// const fromIndex = items.findIndex(
// (item) => item.id === active?.id,
// );
// const toIndex = items.findIndex(
// (item) => item.id === over?.id,
// );
// return arrayMove(items, fromIndex, toIndex);
// });
field.move(fromIndex, toIndex);
}
}}
onDragOver={({ active, over }) => {
if (!over) {
return;
}
if (active?.id === over?.id) {
return;
}
const source = values.find((item) => item.id === active?.id);
if (source && over?.data?.current?.type === 'column') {
source.type = over.id;
return;
}
console.log('active.id', active.id, over?.data?.current);
const target = values.find((item) => item.id === over?.id);
if (source && target) {
if (source.type !== target.type) {
source.type = target.type;
}
}
}}
>
<DragOverlay style={{ pointerEvents: 'none' }}>aaa</DragOverlay>
<div className={'kanban'}>
<div className={'kanban-container'}>
<DndContext
// collisionDetection={closestCorners}
onDragEnd={({ active, over }) => {
const source = values.find((item) => item.id === active?.id);
const target = values.find((item) => item.id === over?.id);
if (source && target) {
const fromIndex = values.findIndex(
(item) => item.id === active?.id,
);
const toIndex = values.findIndex(
(item) => item.id === over?.id,
);
// setValues((items) => {
// const fromIndex = items.findIndex(
// (item) => item.id === active?.id,
// );
// const toIndex = items.findIndex(
// (item) => item.id === over?.id,
// );
// return arrayMove(items, fromIndex, toIndex);
// });
field.move(fromIndex, toIndex);
}
}}
onDragOver={({ active, over }) => {
if (!over) {
return;
}
if (active?.id === over?.id) {
return;
}
const source = values.find((item) => item.id === active?.id);
if (source && over?.data?.current?.type === 'column') {
source[groupName] = over.id;
return;
}
console.log('active.id', active.id, over?.data?.current);
const target = values.find((item) => item.id === over?.id);
if (source && target) {
if (source[groupName] !== target[groupName]) {
source[groupName] = target[groupName];
}
}
}}
>
<DragOverlay style={{ pointerEvents: 'none' }}>aaa</DragOverlay>
<SortableContext
strategy={horizontalListSortingStrategy}
items={columns}
>
{columns.map((column) => (
<SortableItem
key={column.id}
type={'column'}
className={'column'}
id={column.id}
<SortableContext
strategy={horizontalListSortingStrategy}
items={
columns?.map((column) => ({
...column,
id: column.value,
})) || []
}
>
<div className={'column-title'}>{column.title}</div>
<SortableContext
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}
type={'item'}
{columns?.map((column) => (
<SortableItem
id={column.value}
key={column.value}
type={'column'}
className={'column'}
>
<KanbanColumnContext.Provider value={column}>
<div className={'column-title'}>
<Tag color={column.color}>{column.label}</Tag>
</div>
<SortableContext
strategy={verticalListSortingStrategy}
items={column.items}
>
<KanbanCardContext.Provider
value={{
index: index,
record: item,
viewSchema: cardViewSchema,
}}
>
<RecursionField name={index} schema={cardSchema} />
</KanbanCardContext.Provider>
</SortableItem>
);
})}
</SortableContext>
<Button
type={'text'}
icon={<PlusOutlined />}
onClick={() => {
field.push({
id: uid(),
type: column.id,
title: uid(),
});
}}
{column.items.map((item) => {
const index = values.findIndex(
(value) => value.id === item.id,
);
return (
<SortableItem
key={item.id}
className={'item'}
id={item.id}
type={'item'}
>
<KanbanCardContext.Provider
value={{
index: index,
record: item,
viewSchema: cardViewSchema,
}}
>
<RecursionField
name={index}
schema={cardSchema}
/>
</KanbanCardContext.Provider>
</SortableItem>
);
})}
</SortableContext>
<RecursionField
name={addCardSchema?.name}
schema={addCardSchema}
/>
{/* <Button
type={'text'}
icon={<PlusOutlined />}
onClick={() => {
field.push({
id: uid(),
title: uid(),
[groupName]: column.value,
});
}}
>
</Button> */}
</KanbanColumnContext.Provider>
</SortableItem>
))}
<div
id={'addColumn'}
key={'addColumn'}
// type={'column'}
className={'column add-column'}
>
</Button>
</SortableItem>
))}
</SortableContext>
</DndContext>
{/* <span className={'add-column-plus'}/> */}
<span><PlusOutlined/> </span>
</div>
</SortableContext>
</DndContext>
</div>
</div>
</div>
</div>
</KanbanContext.Provider>
);
});
export const Kanban: any = observer((props: any) => {
return (
<CollectionProvider collectionName={props.collectionName}>
<InternalKanban {...props} />
</CollectionProvider>
);
});
Kanban.useCreateAction = () => {
const { service, resource, props } = useKanban();
const column = useContext(KanbanColumnContext);
const groupName = props.groupName;
const form = useForm();
return {
async run() {
await resource.create({
...form.values,
[groupName]: column.value,
});
await form.reset();
return service.refresh();
},
};
};
Kanban.useUpdateAction = () => {
const { service, resource, props } = useKanban();
const ctx = useContext(KanbanCardContext);
const form = useForm();
return {
async run() {
await resource.save(form.values, {
resourceKey: ctx.record.id,
});
await service.refresh();
},
};
};
Kanban.useResource = ({ onSuccess }) => {
const { props } = useKanban();
const { collection } = useCollectionContext();
const ctx = useContext(KanbanCardContext);
const resource = Resource.make({
resourceName: collection?.name || props.collectionName,
resourceKey: ctx?.record?.id,
});
console.log(
'collection?.name || props.collectionName',
collection?.name || props.collectionName,
);
const service = useRequest(
(params?: any) => {
return Promise.resolve(ctx.record);
},
{
// formatResult: (result) => result?.data,
onSuccess,
refreshDeps: [ctx?.record],
// manual,
},
);
return { resource, service, initialValues: service.data, ...service };
};
Kanban.Card = observer((props) => {
const [visible, setVisible] = useState(false);
const { index, viewSchema } = useContext(KanbanCardContext);
const { DesignableBar } = useDesignable();
const { children, ...others } = props;
return (
<>
<VisibleContext.Provider value={[visible, setVisible]}>
<Card
onClick={(e) => {
setVisible(true);
}}
bordered={false}
{...others}
>
{props.children}
{children}
<DesignableBar />
</Card>
<Drawer
width={'50%'}
title={'查看数据'}
visible={visible}
onClose={() => {
setVisible(false);
}}
>
<RecursionField name={index} schema={viewSchema} />
</Drawer>
</>
<RecursionField name={index} schema={viewSchema} />
</VisibleContext.Provider>
);
});
Kanban.Card.View = observer((props) => {
return <div>{props.children}</div>;
});
Kanban.Card.View = Action.Drawer;
Kanban.Card.AddNew = Action;
Kanban.DesignableBar = DesignableBar;
Kanban.Card.DesignableBar = CardDesignableBar;

View File

@ -1,9 +1,38 @@
.column {
position: relative;
width: 25vw;
width: 300px;
background: #f9f9f9;
margin: 0 12px;
margin-right: 24px;
padding: 24px;
&.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);
}
}
}
.column-title {
@ -14,7 +43,12 @@
.kanban {
display: flex;
margin: 0 -12px;
margin: 0;
overflow: auto;
}
.kanban-container {
display: flex;
}
.item {
@ -31,4 +65,42 @@
position: absolute;
top: 0;
right: 0;
}
}
.kanban .ant-card-body {
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;
background: none !important;
border: 2px solid rgba(241, 139, 98, 0.3) !important;
&.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;
}
}
}
}