improve code
This commit is contained in:
parent
754be7d1d1
commit
c23d2c5c3c
@ -38,13 +38,16 @@ export interface SortableItemProps {}
|
|||||||
|
|
||||||
export const SortableItemContext = createContext<any>({});
|
export const SortableItemContext = createContext<any>({});
|
||||||
|
|
||||||
export function DragHandle() {
|
export function DragHandle(props) {
|
||||||
|
const { component, ...others } = props;
|
||||||
|
const Icon = component || DragOutlined;
|
||||||
return (
|
return (
|
||||||
<SortableItemContext.Consumer>
|
<SortableItemContext.Consumer>
|
||||||
{({ setDraggableNodeRef, attributes, listeners }) =>
|
{({ setDraggableNodeRef, attributes, listeners }) =>
|
||||||
setDraggableNodeRef && (
|
setDraggableNodeRef && (
|
||||||
<DragOutlined
|
<Icon
|
||||||
ref={setDraggableNodeRef}
|
ref={setDraggableNodeRef}
|
||||||
|
{...others}
|
||||||
{...attributes}
|
{...attributes}
|
||||||
{...listeners}
|
{...listeners}
|
||||||
/>
|
/>
|
||||||
|
@ -236,22 +236,27 @@ function generateCardItemSchema(component) {
|
|||||||
'x-component': 'Action.Dropdown',
|
'x-component': 'Action.Dropdown',
|
||||||
'x-component-props': {},
|
'x-component-props': {},
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
// [uid()]: {
|
||||||
type: 'void',
|
// type: 'void',
|
||||||
title: '操作 1',
|
// title: '操作 1',
|
||||||
'x-component': 'Menu.Action',
|
// 'x-component': 'Menu.Action',
|
||||||
'x-component-props': {
|
// 'x-component-props': {
|
||||||
style: {
|
// style: {
|
||||||
minWidth: 150,
|
// minWidth: 150,
|
||||||
},
|
// },
|
||||||
disabled: true,
|
// disabled: true,
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
[uid()]: {
|
[uid()]: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: 'action1',
|
name: 'action1',
|
||||||
title: '查看',
|
title: '查看',
|
||||||
'x-component': 'Menu.Action',
|
'x-component': 'Menu.Action',
|
||||||
|
'x-component-props': {
|
||||||
|
style: {
|
||||||
|
minWidth: 150,
|
||||||
|
},
|
||||||
|
},
|
||||||
'x-designable-bar': 'Table.Action.DesignableBar',
|
'x-designable-bar': 'Table.Action.DesignableBar',
|
||||||
'x-action-type': 'view',
|
'x-action-type': 'view',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { forwardRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
observer,
|
observer,
|
||||||
connect,
|
connect,
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
FormConsumer,
|
FormConsumer,
|
||||||
} from '@formily/react';
|
} from '@formily/react';
|
||||||
import { ArrayCollapse, FormLayout } from '@formily/antd';
|
import { ArrayCollapse, FormLayout } from '@formily/antd';
|
||||||
import { uid } from '@formily/shared';
|
import { uid, isValid } from '@formily/shared';
|
||||||
import '@formily/antd/lib/form-tab/style';
|
import '@formily/antd/lib/form-tab/style';
|
||||||
import {
|
import {
|
||||||
Collapse,
|
Collapse,
|
||||||
@ -32,6 +32,7 @@ import {
|
|||||||
DatabaseOutlined,
|
DatabaseOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
|
MenuOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import cls from 'classnames';
|
import cls from 'classnames';
|
||||||
import './style.less';
|
import './style.less';
|
||||||
@ -39,8 +40,53 @@ import Modal from 'antd/lib/modal/Modal';
|
|||||||
import { clone, cloneDeep, get } from 'lodash';
|
import { clone, cloneDeep, get } from 'lodash';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRequest } from 'ahooks';
|
import { useRequest } from 'ahooks';
|
||||||
import { createOrUpdateCollection, deleteCollection } from '..';
|
import {
|
||||||
|
collectionMoveToAfter,
|
||||||
|
createOrUpdateCollection,
|
||||||
|
deleteCollection,
|
||||||
|
} from '..';
|
||||||
import { useCollectionsContext } from '../../constate/Collections';
|
import { useCollectionsContext } from '../../constate/Collections';
|
||||||
|
import {
|
||||||
|
DragHandle,
|
||||||
|
SortableItem,
|
||||||
|
SortableItemContext,
|
||||||
|
} from '../../components/Sortable';
|
||||||
|
import { DndContext, DragOverlay } from '@dnd-kit/core';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
interface SelectOptionProps {
|
||||||
|
id: any;
|
||||||
|
title: string;
|
||||||
|
data?: any;
|
||||||
|
onRemove?: any;
|
||||||
|
showRemove?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectOption(props: SelectOptionProps) {
|
||||||
|
const { id, data, onRemove, showRemove } = props;
|
||||||
|
return (
|
||||||
|
<SortableItem id={id} data={data}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
// justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DragHandle
|
||||||
|
component={forwardRef<any>((props, ref) => {
|
||||||
|
return <MenuOutlined {...props} ref={ref} />;
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<span style={{ width: 8 }} />
|
||||||
|
{data.title}
|
||||||
|
{showRemove && (
|
||||||
|
<DeleteOutlined style={{ marginLeft: 'auto' }} onClick={onRemove} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SortableItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export const DatabaseCollection = observer((props) => {
|
export const DatabaseCollection = observer((props) => {
|
||||||
const field = useField<Formily.Core.Models.ArrayField>();
|
const field = useField<Formily.Core.Models.ArrayField>();
|
||||||
@ -51,6 +97,7 @@ export const DatabaseCollection = observer((props) => {
|
|||||||
const form = useForm();
|
const form = useForm();
|
||||||
const [newValue, setNewValue] = useState('');
|
const [newValue, setNewValue] = useState('');
|
||||||
const { loading, refresh, collections = [] } = useCollectionsContext();
|
const { loading, refresh, collections = [] } = useCollectionsContext();
|
||||||
|
const [dragOverlayContent, setDragOverlayContent] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
field.setValue(collections);
|
field.setValue(collections);
|
||||||
@ -87,6 +134,27 @@ export const DatabaseCollection = observer((props) => {
|
|||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
<div style={{ textAlign: 'center' }}>
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<DndContext
|
||||||
|
onDragStart={(event) => {
|
||||||
|
setDragOverlayContent(event.active?.data?.current?.title || '');
|
||||||
|
}}
|
||||||
|
onDragEnd={async (event) => {
|
||||||
|
const sourceName = event.active?.data?.current?.name;
|
||||||
|
const targetName = event.over?.data?.current?.name;
|
||||||
|
console.log({ sourceName, targetName });
|
||||||
|
await collectionMoveToAfter(sourceName, targetName);
|
||||||
|
await refresh();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{createPortal(
|
||||||
|
<DragOverlay
|
||||||
|
zIndex={2000}
|
||||||
|
style={{ pointerEvents: 'none', whiteSpace: 'nowrap' }}
|
||||||
|
>
|
||||||
|
{dragOverlayContent}
|
||||||
|
</DragOverlay>,
|
||||||
|
document.body,
|
||||||
|
)}
|
||||||
<Select
|
<Select
|
||||||
loading={loading}
|
loading={loading}
|
||||||
value={activeIndex}
|
value={activeIndex}
|
||||||
@ -144,18 +212,15 @@ export const DatabaseCollection = observer((props) => {
|
|||||||
item.unsaved ? ' (未保存)' : ''
|
item.unsaved ? ' (未保存)' : ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<SelectOption
|
||||||
style={{
|
id={item.name}
|
||||||
display: 'flex',
|
title={item.title || '未命名'}
|
||||||
justifyContent: 'space-between',
|
data={{
|
||||||
alignItems: 'center',
|
title: item.title,
|
||||||
|
name: item.name,
|
||||||
}}
|
}}
|
||||||
>
|
showRemove={item.privilege !== 'undelete'}
|
||||||
{item.title || '未命名'}{' '}
|
onRemove={async (e) => {
|
||||||
{item.unsaved ? '(未保存)' : ''}
|
|
||||||
{item.privilege !== 'undelete' && (
|
|
||||||
<DeleteOutlined
|
|
||||||
onClick={async (e) => {
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
field.remove(index);
|
field.remove(index);
|
||||||
if (field.value?.length === 0) {
|
if (field.value?.length === 0) {
|
||||||
@ -176,12 +241,11 @@ export const DatabaseCollection = observer((props) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Select>
|
</Select>
|
||||||
|
</DndContext>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
visible={visible}
|
visible={visible}
|
||||||
@ -228,8 +292,30 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
const [activeKey, setActiveKey] = useState(null);
|
const [activeKey, setActiveKey] = useState(null);
|
||||||
|
const [dragOverlayContent, setDragOverlayContent] = useState('');
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<DndContext
|
||||||
|
onDragStart={(event) => {
|
||||||
|
setDragOverlayContent(event.active?.data?.current?.title || '');
|
||||||
|
}}
|
||||||
|
onDragEnd={async (event) => {
|
||||||
|
const fromIndex = event.active?.data?.current?.index;
|
||||||
|
const toIndex = event.over?.data?.current?.index;
|
||||||
|
if (isValid(fromIndex) && isValid(toIndex)) {
|
||||||
|
field.move(fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{createPortal(
|
||||||
|
<DragOverlay
|
||||||
|
zIndex={2000}
|
||||||
|
style={{ pointerEvents: 'none', whiteSpace: 'nowrap' }}
|
||||||
|
>
|
||||||
|
{dragOverlayContent}
|
||||||
|
</DragOverlay>,
|
||||||
|
document.body,
|
||||||
|
)}
|
||||||
<Collapse
|
<Collapse
|
||||||
activeKey={activeKey}
|
activeKey={activeKey}
|
||||||
onChange={(key) => {
|
onChange={(key) => {
|
||||||
@ -255,19 +341,31 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
return (
|
return (
|
||||||
<Collapse.Panel
|
<Collapse.Panel
|
||||||
header={
|
header={
|
||||||
<>
|
<SortableItem
|
||||||
|
id={item.key}
|
||||||
|
className={'sortable-item'}
|
||||||
|
data={{
|
||||||
|
index,
|
||||||
|
title: item?.uiSchema?.title,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DragHandle className={'drag-handle'} />
|
||||||
{(item.uiSchema && item.uiSchema.title) || (
|
{(item.uiSchema && item.uiSchema.title) || (
|
||||||
<i style={{ color: 'rgba(0, 0, 0, 0.25)' }}>未命名</i>
|
<i style={{ color: 'rgba(0, 0, 0, 0.25)' }}>未命名</i>
|
||||||
)}{' '}
|
)}{' '}
|
||||||
<Tag
|
<Tag
|
||||||
className={item.privilege ? cls(item.privilege) : undefined}
|
className={
|
||||||
|
item.privilege ? cls(item.privilege) : undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{schema.title}
|
{schema.title}
|
||||||
</Tag>
|
</Tag>
|
||||||
<span style={{ color: 'rgba(0, 0, 0, 0.25)', fontSize: 14 }}>
|
<span
|
||||||
|
style={{ color: 'rgba(0, 0, 0, 0.25)', fontSize: 14 }}
|
||||||
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</SortableItem>
|
||||||
}
|
}
|
||||||
extra={
|
extra={
|
||||||
item.privilege === 'undelete'
|
item.privilege === 'undelete'
|
||||||
@ -284,6 +382,7 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
key={item.key}
|
key={item.key}
|
||||||
|
forceRender
|
||||||
>
|
>
|
||||||
<RecursionField
|
<RecursionField
|
||||||
key={`${item.key}_${index}`}
|
key={`${item.key}_${index}`}
|
||||||
@ -310,6 +409,7 @@ export const DatabaseField: any = observer((props) => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
</DndContext>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
placement={'bottomCenter'}
|
placement={'bottomCenter'}
|
||||||
overlayClassName={'all-fields'}
|
overlayClassName={'all-fields'}
|
||||||
|
@ -26,3 +26,28 @@
|
|||||||
color: rgba(0, 0, 0, 0.25);
|
color: rgba(0, 0, 0, 0.25);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-collapse-header {
|
||||||
|
position: relative;
|
||||||
|
&:hover {
|
||||||
|
.drag-handle {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.drag-handle {
|
||||||
|
opacity: 0;
|
||||||
|
margin-right: 6px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
.ant-collapse-arrow {
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
}
|
||||||
|
.ant-collapse-extra {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 12px;
|
||||||
|
}
|
||||||
|
}
|
@ -60,6 +60,20 @@ export async function createSchema(schema: ISchema) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function collectionMoveToAfter(source, target) {
|
||||||
|
if (source && target) {
|
||||||
|
return request(`collections:sort/${source}`, {
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
field: 'sort',
|
||||||
|
target: {
|
||||||
|
name: target,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateSchema(schema: ISchema) {
|
export async function updateSchema(schema: ISchema) {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return;
|
return;
|
||||||
|
@ -46,6 +46,9 @@ export const Tabs: any = observer((props: any) => {
|
|||||||
if (!path1 || !path2) {
|
if (!path1 || !path2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (path1.join('.') === path2.join('.')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const data = findPropertyByPath(root, path1);
|
const data = findPropertyByPath(root, path1);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return;
|
return;
|
||||||
|
@ -6,7 +6,7 @@ import { cloneDeep, omit } from 'lodash';
|
|||||||
export const findAll = async (ctx: actions.Context, next: actions.Next) => {
|
export const findAll = async (ctx: actions.Context, next: actions.Next) => {
|
||||||
const Collection = ctx.db.getModel('collections');
|
const Collection = ctx.db.getModel('collections');
|
||||||
const collections = await Collection.findAll(Collection.parseApiJson({
|
const collections = await Collection.findAll(Collection.parseApiJson({
|
||||||
sort: '-created_at',
|
sort: 'sort',
|
||||||
}));
|
}));
|
||||||
const data = [];
|
const data = [];
|
||||||
for (const collection of collections) {
|
for (const collection of collections) {
|
||||||
@ -29,6 +29,14 @@ export const createOrUpdate = async (ctx: actions.Context, next: actions.Next) =
|
|||||||
} else {
|
} else {
|
||||||
await collection.update(values);
|
await collection.update(values);
|
||||||
}
|
}
|
||||||
|
if (values.fields) {
|
||||||
|
values.fields = values.fields.map((field, index) => {
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
sort: index + 1,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
await collection.updateAssociations(values);
|
await collection.updateAssociations(values);
|
||||||
await collection.migrate();
|
await collection.migrate();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user