improve code
This commit is contained in:
		
							parent
							
								
									754be7d1d1
								
							
						
					
					
						commit
						c23d2c5c3c
					
				@ -38,13 +38,16 @@ export interface SortableItemProps {}
 | 
			
		||||
 | 
			
		||||
export const SortableItemContext = createContext<any>({});
 | 
			
		||||
 | 
			
		||||
export function DragHandle() {
 | 
			
		||||
export function DragHandle(props) {
 | 
			
		||||
  const { component, ...others } = props;
 | 
			
		||||
  const Icon = component || DragOutlined;
 | 
			
		||||
  return (
 | 
			
		||||
    <SortableItemContext.Consumer>
 | 
			
		||||
      {({ setDraggableNodeRef, attributes, listeners }) =>
 | 
			
		||||
        setDraggableNodeRef && (
 | 
			
		||||
          <DragOutlined
 | 
			
		||||
          <Icon
 | 
			
		||||
            ref={setDraggableNodeRef}
 | 
			
		||||
            {...others}
 | 
			
		||||
            {...attributes}
 | 
			
		||||
            {...listeners}
 | 
			
		||||
          />
 | 
			
		||||
 | 
			
		||||
@ -236,22 +236,27 @@ function generateCardItemSchema(component) {
 | 
			
		||||
                  'x-component': 'Action.Dropdown',
 | 
			
		||||
                  'x-component-props': {},
 | 
			
		||||
                  properties: {
 | 
			
		||||
                    [uid()]: {
 | 
			
		||||
                      type: 'void',
 | 
			
		||||
                      title: '操作 1',
 | 
			
		||||
                      'x-component': 'Menu.Action',
 | 
			
		||||
                      'x-component-props': {
 | 
			
		||||
                        style: {
 | 
			
		||||
                          minWidth: 150,
 | 
			
		||||
                        },
 | 
			
		||||
                        disabled: true,
 | 
			
		||||
                      },
 | 
			
		||||
                    },
 | 
			
		||||
                    // [uid()]: {
 | 
			
		||||
                    //   type: 'void',
 | 
			
		||||
                    //   title: '操作 1',
 | 
			
		||||
                    //   'x-component': 'Menu.Action',
 | 
			
		||||
                    //   'x-component-props': {
 | 
			
		||||
                    //     style: {
 | 
			
		||||
                    //       minWidth: 150,
 | 
			
		||||
                    //     },
 | 
			
		||||
                    //     disabled: true,
 | 
			
		||||
                    //   },
 | 
			
		||||
                    // },
 | 
			
		||||
                    [uid()]: {
 | 
			
		||||
                      type: 'void',
 | 
			
		||||
                      name: 'action1',
 | 
			
		||||
                      title: '查看',
 | 
			
		||||
                      'x-component': 'Menu.Action',
 | 
			
		||||
                      'x-component-props': {
 | 
			
		||||
                        style: {
 | 
			
		||||
                          minWidth: 150,
 | 
			
		||||
                        },
 | 
			
		||||
                      },
 | 
			
		||||
                      'x-designable-bar': 'Table.Action.DesignableBar',
 | 
			
		||||
                      'x-action-type': 'view',
 | 
			
		||||
                      properties: {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import React, { useState } from 'react';
 | 
			
		||||
import React, { forwardRef, useState } from 'react';
 | 
			
		||||
import {
 | 
			
		||||
  observer,
 | 
			
		||||
  connect,
 | 
			
		||||
@ -11,7 +11,7 @@ import {
 | 
			
		||||
  FormConsumer,
 | 
			
		||||
} from '@formily/react';
 | 
			
		||||
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 {
 | 
			
		||||
  Collapse,
 | 
			
		||||
@ -32,6 +32,7 @@ import {
 | 
			
		||||
  DatabaseOutlined,
 | 
			
		||||
  PlusOutlined,
 | 
			
		||||
  CloseOutlined,
 | 
			
		||||
  MenuOutlined,
 | 
			
		||||
} from '@ant-design/icons';
 | 
			
		||||
import cls from 'classnames';
 | 
			
		||||
import './style.less';
 | 
			
		||||
@ -39,8 +40,53 @@ import Modal from 'antd/lib/modal/Modal';
 | 
			
		||||
import { clone, cloneDeep, get } from 'lodash';
 | 
			
		||||
import { useEffect } from 'react';
 | 
			
		||||
import { useRequest } from 'ahooks';
 | 
			
		||||
import { createOrUpdateCollection, deleteCollection } from '..';
 | 
			
		||||
import {
 | 
			
		||||
  collectionMoveToAfter,
 | 
			
		||||
  createOrUpdateCollection,
 | 
			
		||||
  deleteCollection,
 | 
			
		||||
} from '..';
 | 
			
		||||
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) => {
 | 
			
		||||
  const field = useField<Formily.Core.Models.ArrayField>();
 | 
			
		||||
@ -51,6 +97,7 @@ export const DatabaseCollection = observer((props) => {
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  const [newValue, setNewValue] = useState('');
 | 
			
		||||
  const { loading, refresh, collections = [] } = useCollectionsContext();
 | 
			
		||||
  const [dragOverlayContent, setDragOverlayContent] = useState('');
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    field.setValue(collections);
 | 
			
		||||
@ -87,6 +134,27 @@ export const DatabaseCollection = observer((props) => {
 | 
			
		||||
        }}
 | 
			
		||||
        title={
 | 
			
		||||
          <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
 | 
			
		||||
                loading={loading}
 | 
			
		||||
                value={activeIndex}
 | 
			
		||||
@ -144,18 +212,15 @@ export const DatabaseCollection = observer((props) => {
 | 
			
		||||
                        item.unsaved ? ' (未保存)' : ''
 | 
			
		||||
                      }`}
 | 
			
		||||
                    >
 | 
			
		||||
                    <div
 | 
			
		||||
                      style={{
 | 
			
		||||
                        display: 'flex',
 | 
			
		||||
                        justifyContent: 'space-between',
 | 
			
		||||
                        alignItems: 'center',
 | 
			
		||||
                      <SelectOption
 | 
			
		||||
                        id={item.name}
 | 
			
		||||
                        title={item.title || '未命名'}
 | 
			
		||||
                        data={{
 | 
			
		||||
                          title: item.title,
 | 
			
		||||
                          name: item.name,
 | 
			
		||||
                        }}
 | 
			
		||||
                    >
 | 
			
		||||
                      {item.title || '未命名'}{' '}
 | 
			
		||||
                      {item.unsaved ? '(未保存)' : ''}
 | 
			
		||||
                      {item.privilege !== 'undelete' && (
 | 
			
		||||
                        <DeleteOutlined
 | 
			
		||||
                          onClick={async (e) => {
 | 
			
		||||
                        showRemove={item.privilege !== 'undelete'}
 | 
			
		||||
                        onRemove={async (e) => {
 | 
			
		||||
                          e.stopPropagation();
 | 
			
		||||
                          field.remove(index);
 | 
			
		||||
                          if (field.value?.length === 0) {
 | 
			
		||||
@ -176,12 +241,11 @@ export const DatabaseCollection = observer((props) => {
 | 
			
		||||
                          }
 | 
			
		||||
                        }}
 | 
			
		||||
                      />
 | 
			
		||||
                      )}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    </Select.Option>
 | 
			
		||||
                  );
 | 
			
		||||
                })}
 | 
			
		||||
              </Select>
 | 
			
		||||
            </DndContext>
 | 
			
		||||
          </div>
 | 
			
		||||
        }
 | 
			
		||||
        visible={visible}
 | 
			
		||||
@ -228,8 +292,30 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
    }
 | 
			
		||||
  }, []);
 | 
			
		||||
  const [activeKey, setActiveKey] = useState(null);
 | 
			
		||||
  const [dragOverlayContent, setDragOverlayContent] = useState('');
 | 
			
		||||
  return (
 | 
			
		||||
    <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
 | 
			
		||||
          activeKey={activeKey}
 | 
			
		||||
          onChange={(key) => {
 | 
			
		||||
@ -255,19 +341,31 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
            return (
 | 
			
		||||
              <Collapse.Panel
 | 
			
		||||
                header={
 | 
			
		||||
                <>
 | 
			
		||||
                  <SortableItem
 | 
			
		||||
                    id={item.key}
 | 
			
		||||
                    className={'sortable-item'}
 | 
			
		||||
                    data={{
 | 
			
		||||
                      index,
 | 
			
		||||
                      title: item?.uiSchema?.title,
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    <DragHandle className={'drag-handle'} />
 | 
			
		||||
                    {(item.uiSchema && item.uiSchema.title) || (
 | 
			
		||||
                      <i style={{ color: 'rgba(0, 0, 0, 0.25)' }}>未命名</i>
 | 
			
		||||
                    )}{' '}
 | 
			
		||||
                    <Tag
 | 
			
		||||
                    className={item.privilege ? cls(item.privilege) : undefined}
 | 
			
		||||
                      className={
 | 
			
		||||
                        item.privilege ? cls(item.privilege) : undefined
 | 
			
		||||
                      }
 | 
			
		||||
                    >
 | 
			
		||||
                      {schema.title}
 | 
			
		||||
                    </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}
 | 
			
		||||
                    </span>
 | 
			
		||||
                </>
 | 
			
		||||
                  </SortableItem>
 | 
			
		||||
                }
 | 
			
		||||
                extra={
 | 
			
		||||
                  item.privilege === 'undelete'
 | 
			
		||||
@ -284,6 +382,7 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
                      ]
 | 
			
		||||
                }
 | 
			
		||||
                key={item.key}
 | 
			
		||||
                forceRender
 | 
			
		||||
              >
 | 
			
		||||
                <RecursionField
 | 
			
		||||
                  key={`${item.key}_${index}`}
 | 
			
		||||
@ -310,6 +409,7 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
            );
 | 
			
		||||
          })}
 | 
			
		||||
        </Collapse>
 | 
			
		||||
      </DndContext>
 | 
			
		||||
      <Dropdown
 | 
			
		||||
        placement={'bottomCenter'}
 | 
			
		||||
        overlayClassName={'all-fields'}
 | 
			
		||||
 | 
			
		||||
@ -26,3 +26,28 @@
 | 
			
		||||
  color: rgba(0, 0, 0, 0.25);
 | 
			
		||||
  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) {
 | 
			
		||||
  if (!schema) {
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
@ -46,6 +46,9 @@ export const Tabs: any = observer((props: any) => {
 | 
			
		||||
    if (!path1 || !path2) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (path1.join('.') === path2.join('.')) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const data = findPropertyByPath(root, path1);
 | 
			
		||||
    if (!data) {
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ import { cloneDeep, omit } from 'lodash';
 | 
			
		||||
export const findAll = async (ctx: actions.Context, next: actions.Next) => {
 | 
			
		||||
  const Collection = ctx.db.getModel('collections');
 | 
			
		||||
  const collections = await Collection.findAll(Collection.parseApiJson({
 | 
			
		||||
    sort: '-created_at',
 | 
			
		||||
    sort: 'sort',
 | 
			
		||||
  }));
 | 
			
		||||
  const data = [];
 | 
			
		||||
  for (const collection of collections) {
 | 
			
		||||
@ -29,6 +29,14 @@ export const createOrUpdate = async (ctx: actions.Context, next: actions.Next) =
 | 
			
		||||
    } else {
 | 
			
		||||
      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.migrate();
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user