updates...
This commit is contained in:
		
							parent
							
								
									057dc8406a
								
							
						
					
					
						commit
						63538cf240
					
				@ -1,68 +0,0 @@
 | 
			
		||||
import { DatabaseOutlined } from '@ant-design/icons';
 | 
			
		||||
import { SchemaRenderer } from '../../../';
 | 
			
		||||
import Modal from 'antd/lib/modal/Modal';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { useState } from 'react';
 | 
			
		||||
import schema from './schema';
 | 
			
		||||
import { Button, Dropdown, Menu } from 'antd';
 | 
			
		||||
import { useMemo } from 'react';
 | 
			
		||||
import { createForm } from '@formily/core';
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  const form = useMemo(
 | 
			
		||||
    () =>
 | 
			
		||||
      createForm({
 | 
			
		||||
        initialValues: {
 | 
			
		||||
          title: '数据表名称',
 | 
			
		||||
        },
 | 
			
		||||
      }),
 | 
			
		||||
    [],
 | 
			
		||||
  );
 | 
			
		||||
  const [visible, setVisible] = useState(false);
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Button
 | 
			
		||||
        onClick={() => {
 | 
			
		||||
          setVisible(true);
 | 
			
		||||
        }}
 | 
			
		||||
        type={'primary'}
 | 
			
		||||
      >
 | 
			
		||||
        <DatabaseOutlined />
 | 
			
		||||
      </Button>
 | 
			
		||||
      <Modal
 | 
			
		||||
        title={
 | 
			
		||||
          <>
 | 
			
		||||
            <Dropdown
 | 
			
		||||
              overlay={
 | 
			
		||||
                <Menu
 | 
			
		||||
                  onClick={(info) => {
 | 
			
		||||
                    form.setValues({
 | 
			
		||||
                      title: `数据表${info.key}`,
 | 
			
		||||
                    });
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  <Menu.Item key={1}>数据表1</Menu.Item>
 | 
			
		||||
                  <Menu.Item key={2}>数据表2</Menu.Item>
 | 
			
		||||
                  <Menu.Divider></Menu.Divider>
 | 
			
		||||
                  <Menu.Item key={5}>新建数据表</Menu.Item>
 | 
			
		||||
                </Menu>
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
              <span style={{cursor: 'pointer'}}>数据表3</span>
 | 
			
		||||
            </Dropdown>
 | 
			
		||||
          </>
 | 
			
		||||
        }
 | 
			
		||||
        visible={visible}
 | 
			
		||||
        onOk={async () => {
 | 
			
		||||
          await form.submit();
 | 
			
		||||
          setVisible(false);
 | 
			
		||||
        }}
 | 
			
		||||
        onCancel={() => {
 | 
			
		||||
          setVisible(false);
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <SchemaRenderer form={form} schema={schema} />
 | 
			
		||||
      </Modal>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@ -1,49 +0,0 @@
 | 
			
		||||
import { ISchema } from "@formily/react";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  type: 'object',
 | 
			
		||||
  properties: {
 | 
			
		||||
    form: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      "x-component": 'FormLayout',
 | 
			
		||||
      "x-component-props": {
 | 
			
		||||
        layout: 'vertical',
 | 
			
		||||
      },
 | 
			
		||||
      properties: {
 | 
			
		||||
        title: {
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          title: '数据表名称',
 | 
			
		||||
          required: true,
 | 
			
		||||
          'x-decorator': 'FormItem',
 | 
			
		||||
          'x-component': 'Input',
 | 
			
		||||
        },
 | 
			
		||||
        fields: {
 | 
			
		||||
          type: 'array',
 | 
			
		||||
          title: '字段',
 | 
			
		||||
          'x-decorator': 'FormItem',
 | 
			
		||||
          'x-component': 'DatabaseField',
 | 
			
		||||
          default: [
 | 
			
		||||
            {
 | 
			
		||||
              id: 1,
 | 
			
		||||
              interface: 'string',
 | 
			
		||||
              dataType: 'string',
 | 
			
		||||
              name: 'title',
 | 
			
		||||
              ui: {
 | 
			
		||||
                title: '标题',
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              id: 2,
 | 
			
		||||
              dataType: 'text',
 | 
			
		||||
              interface: 'textarea',
 | 
			
		||||
              name: 'content',
 | 
			
		||||
              ui: {
 | 
			
		||||
                title: '内容',
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
} as ISchema;
 | 
			
		||||
@ -1,56 +0,0 @@
 | 
			
		||||
 | 
			
		||||
export const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  // title: 'ModalForm',
 | 
			
		||||
  'x-component': 'Action',
 | 
			
		||||
  'x-component-props': {
 | 
			
		||||
    type: 'primary',
 | 
			
		||||
    icon: 'DatabaseOutlined',
 | 
			
		||||
  },
 | 
			
		||||
  properties: {
 | 
			
		||||
    modal1: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      title: '对话框标题',
 | 
			
		||||
      'x-decorator': 'Form',
 | 
			
		||||
      'x-component': 'Action.Modal',
 | 
			
		||||
      properties: {
 | 
			
		||||
        title: {
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          title: '数据表名称',
 | 
			
		||||
          required: true,
 | 
			
		||||
          'x-decorator': 'FormItem',
 | 
			
		||||
          'x-component': 'Input',
 | 
			
		||||
        },
 | 
			
		||||
        fields: {
 | 
			
		||||
          type: 'array',
 | 
			
		||||
          title: '字段',
 | 
			
		||||
          'x-decorator': 'FormItem',
 | 
			
		||||
          'x-component': 'DatabaseField',
 | 
			
		||||
          default: [
 | 
			
		||||
            {
 | 
			
		||||
              id: 1,
 | 
			
		||||
              interface: 'string',
 | 
			
		||||
              dataType: 'string',
 | 
			
		||||
              name: 'title',
 | 
			
		||||
              ui: {
 | 
			
		||||
                title: '标题',
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              id: 2,
 | 
			
		||||
              dataType: 'text',
 | 
			
		||||
              interface: 'textarea',
 | 
			
		||||
              name: 'content',
 | 
			
		||||
              ui: {
 | 
			
		||||
                title: '内容',
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default schema;
 | 
			
		||||
							
								
								
									
										45
									
								
								packages/client/src/components/admin-layout/datatable.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								packages/client/src/components/admin-layout/datatable.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,45 @@
 | 
			
		||||
import { SchemaRenderer } from '@nocobase/client/lib';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  const schema = {
 | 
			
		||||
    type: 'array',
 | 
			
		||||
    name: 'collections',
 | 
			
		||||
    'x-component': 'DatabaseCollection',
 | 
			
		||||
    'x-component-props': {},
 | 
			
		||||
    default: [
 | 
			
		||||
      {
 | 
			
		||||
        name: 'test1',
 | 
			
		||||
        title: '数据表 1',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: 'test2',
 | 
			
		||||
        title: '数据表 2',
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    properties: {
 | 
			
		||||
      title: {
 | 
			
		||||
        type: 'string',
 | 
			
		||||
        title: '数据表名称',
 | 
			
		||||
        required: true,
 | 
			
		||||
        'x-decorator': 'FormItem',
 | 
			
		||||
        'x-component': 'Input',
 | 
			
		||||
      },
 | 
			
		||||
      name: {
 | 
			
		||||
        type: 'string',
 | 
			
		||||
        title: '数据表标识',
 | 
			
		||||
        'x-decorator': 'FormItem',
 | 
			
		||||
        'x-component': 'Input',
 | 
			
		||||
        'x-read-pretty': true,
 | 
			
		||||
      },
 | 
			
		||||
      fields: {
 | 
			
		||||
        type: 'array',
 | 
			
		||||
        title: '数据表字段',
 | 
			
		||||
        'x-decorator': 'FormItem',
 | 
			
		||||
        'x-component': 'DatabaseField',
 | 
			
		||||
        default: [],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
  return <SchemaRenderer schema={schema} />;
 | 
			
		||||
};
 | 
			
		||||
@ -23,7 +23,7 @@ import './style.less';
 | 
			
		||||
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
import { ISchema } from '@formily/react';
 | 
			
		||||
import DataTableConfig from './DataTableConfig';
 | 
			
		||||
import Database from './datatable';
 | 
			
		||||
 | 
			
		||||
function LayoutWithMenu({ schema }) {
 | 
			
		||||
  const match = useRouteMatch<any>();
 | 
			
		||||
@ -53,7 +53,7 @@ function LayoutWithMenu({ schema }) {
 | 
			
		||||
            selectedKeys: [activeKey].filter(Boolean),
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
        <DataTableConfig />
 | 
			
		||||
        <Database />
 | 
			
		||||
      </Layout.Header>
 | 
			
		||||
      <Layout>
 | 
			
		||||
        <Layout.Sider
 | 
			
		||||
 | 
			
		||||
@ -18,20 +18,20 @@
 | 
			
		||||
//   }
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
.ant-collapse {
 | 
			
		||||
  border: 1px solid #d9d9d9 !important;
 | 
			
		||||
  border-bottom: 0 !important;
 | 
			
		||||
  > .ant-collapse-item {
 | 
			
		||||
    border-bottom: 1px solid #d9d9d9 !important;
 | 
			
		||||
  }
 | 
			
		||||
  .ant-collapse-content {
 | 
			
		||||
    border-top: 1px solid #d9d9d9 !important;
 | 
			
		||||
  }
 | 
			
		||||
  .ant-collapse-content > .ant-collapse-content-box {
 | 
			
		||||
    padding: 24px !important;
 | 
			
		||||
    padding-bottom: 0 !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// .ant-collapse {
 | 
			
		||||
//   border: 1px solid #d9d9d9 !important;
 | 
			
		||||
//   border-bottom: 0 !important;
 | 
			
		||||
//   > .ant-collapse-item {
 | 
			
		||||
//     border-bottom: 1px solid #d9d9d9 !important;
 | 
			
		||||
//   }
 | 
			
		||||
//   .ant-collapse-content {
 | 
			
		||||
//     border-top: 1px solid #d9d9d9 !important;
 | 
			
		||||
//   }
 | 
			
		||||
//   .ant-collapse-content > .ant-collapse-content-box {
 | 
			
		||||
//     padding: 24px !important;
 | 
			
		||||
//     padding-bottom: 0 !important;
 | 
			
		||||
//   }
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
.database-sider {
 | 
			
		||||
  background: #fafafa;
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ import { AddNew } from '../../schemas/add-new';
 | 
			
		||||
import { Cascader } from '../../schemas/cascader';
 | 
			
		||||
import { Checkbox } from '../../schemas/checkbox';
 | 
			
		||||
import { ColorSelect } from '../../schemas/color-select';
 | 
			
		||||
import { DatabaseField } from '../../schemas/database-field';
 | 
			
		||||
import { DatabaseField, DatabaseCollection } from '../../schemas/database-field';
 | 
			
		||||
import { DatePicker } from '../../schemas/date-picker';
 | 
			
		||||
import { Filter } from '../../schemas/filter';
 | 
			
		||||
import { Form } from '../../schemas/form';
 | 
			
		||||
@ -79,6 +79,7 @@ export const SchemaField = createSchemaField({
 | 
			
		||||
    Checkbox,
 | 
			
		||||
    ColorSelect,
 | 
			
		||||
    DatabaseField,
 | 
			
		||||
    DatabaseCollection,
 | 
			
		||||
    DatePicker,
 | 
			
		||||
    Filter,
 | 
			
		||||
    Form,
 | 
			
		||||
 | 
			
		||||
@ -1,20 +1,23 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { createForm } from '@formily/core';
 | 
			
		||||
import { SchemaRenderer } from '../../';
 | 
			
		||||
import { observer, connect, useField } from '@formily/react';
 | 
			
		||||
import { observer, connect, useField, ISchema } from '@formily/react';
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: 'ModalForm',
 | 
			
		||||
  'x-component': 'Action',
 | 
			
		||||
const schema: ISchema = {
 | 
			
		||||
  type: 'array',
 | 
			
		||||
  name: 'collections',
 | 
			
		||||
  'x-component': 'DatabaseCollection',
 | 
			
		||||
  'x-component-props': {},
 | 
			
		||||
  properties: {
 | 
			
		||||
    modal1: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      title: '对话框标题',
 | 
			
		||||
      'x-decorator': 'Form',
 | 
			
		||||
      'x-component': 'Action.Modal',
 | 
			
		||||
  default: [
 | 
			
		||||
    {
 | 
			
		||||
      name: 'test1',
 | 
			
		||||
      title: '数据表 1',
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      name: 'test2',
 | 
			
		||||
      title: '数据表 2',
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  properties: {
 | 
			
		||||
    title: {
 | 
			
		||||
      type: 'string',
 | 
			
		||||
@ -23,33 +26,21 @@ const schema = {
 | 
			
		||||
      'x-decorator': 'FormItem',
 | 
			
		||||
      'x-component': 'Input',
 | 
			
		||||
    },
 | 
			
		||||
    name: {
 | 
			
		||||
      type: 'string',
 | 
			
		||||
      title: '数据表标识',
 | 
			
		||||
      'x-decorator': 'FormItem',
 | 
			
		||||
      'x-component': 'Input',
 | 
			
		||||
      "x-read-pretty": true,
 | 
			
		||||
    },
 | 
			
		||||
    fields: {
 | 
			
		||||
      type: 'array',
 | 
			
		||||
      title: '数据表字段',
 | 
			
		||||
      'x-decorator': 'FormItem',
 | 
			
		||||
      'x-component': 'DatabaseField',
 | 
			
		||||
          default: [
 | 
			
		||||
            {
 | 
			
		||||
              id: 1,
 | 
			
		||||
              interface: 'string',
 | 
			
		||||
              dataType: 'string',
 | 
			
		||||
              name: 'title',
 | 
			
		||||
              ui: {
 | 
			
		||||
                title: '标题',
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
              id: 2,
 | 
			
		||||
              dataType: 'text',
 | 
			
		||||
              interface: 'textarea',
 | 
			
		||||
              name: 'content',
 | 
			
		||||
              ui: {
 | 
			
		||||
                title: '内容',
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
      default: [],
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const form = createForm();
 | 
			
		||||
 | 
			
		||||
@ -7,17 +7,177 @@ import {
 | 
			
		||||
  ISchema,
 | 
			
		||||
  Schema,
 | 
			
		||||
  useFieldSchema,
 | 
			
		||||
  useForm,
 | 
			
		||||
} from '@formily/react';
 | 
			
		||||
import { ArrayCollapse } from '@formily/antd';
 | 
			
		||||
import { ArrayCollapse, FormLayout } from '@formily/antd';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
import '@formily/antd/lib/form-tab/style';
 | 
			
		||||
import { Collapse, Button, Dropdown, Menu, Tag } from 'antd';
 | 
			
		||||
import {
 | 
			
		||||
  Collapse,
 | 
			
		||||
  Button,
 | 
			
		||||
  Dropdown,
 | 
			
		||||
  Menu,
 | 
			
		||||
  Tag,
 | 
			
		||||
  Select,
 | 
			
		||||
  Divider,
 | 
			
		||||
  Input,
 | 
			
		||||
} from 'antd';
 | 
			
		||||
import { options, interfaces } from './interfaces';
 | 
			
		||||
import { DeleteOutlined } from '@ant-design/icons';
 | 
			
		||||
import {
 | 
			
		||||
  DeleteOutlined,
 | 
			
		||||
  DatabaseOutlined,
 | 
			
		||||
  PlusOutlined,
 | 
			
		||||
  CloseOutlined,
 | 
			
		||||
} from '@ant-design/icons';
 | 
			
		||||
import cls from 'classnames';
 | 
			
		||||
import './style.less';
 | 
			
		||||
import Modal from 'antd/lib/modal/Modal';
 | 
			
		||||
import { get } from 'lodash';
 | 
			
		||||
import { useEffect } from 'react';
 | 
			
		||||
import { useRequest } from 'ahooks';
 | 
			
		||||
import { createOrUpdateCollection, deleteCollection } from '..';
 | 
			
		||||
 | 
			
		||||
export const DatabaseCollection = observer((props) => {
 | 
			
		||||
  const field = useField<Formily.Core.Models.ArrayField>();
 | 
			
		||||
  const [visible, setVisible] = useState(false);
 | 
			
		||||
  const [open, setOpen] = useState(false);
 | 
			
		||||
  const schema = useFieldSchema();
 | 
			
		||||
  const [activeIndex, setActiveIndex] = useState(0);
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  const [newValue, setNewValue] = useState('');
 | 
			
		||||
 | 
			
		||||
  useRequest('collections:list?sort=-created_at', {
 | 
			
		||||
    formatResult: (result) => result?.data,
 | 
			
		||||
    onSuccess(data) {
 | 
			
		||||
      field.setValue(data);
 | 
			
		||||
      console.log('onSuccess', data);
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Button
 | 
			
		||||
        onClick={() => {
 | 
			
		||||
          setVisible(true);
 | 
			
		||||
        }}
 | 
			
		||||
        type={'primary'}
 | 
			
		||||
      >
 | 
			
		||||
        <DatabaseOutlined />
 | 
			
		||||
      </Button>
 | 
			
		||||
      <Modal
 | 
			
		||||
        title={
 | 
			
		||||
          <div style={{ textAlign: 'center' }}>
 | 
			
		||||
            <Select
 | 
			
		||||
              value={activeIndex}
 | 
			
		||||
              style={{ minWidth: 300, textAlign: 'center' }}
 | 
			
		||||
              onChange={(value) => {
 | 
			
		||||
                setActiveIndex(value as any);
 | 
			
		||||
              }}
 | 
			
		||||
              open={open}
 | 
			
		||||
              onDropdownVisibleChange={setOpen}
 | 
			
		||||
              optionLabelProp={'label'}
 | 
			
		||||
              dropdownRender={(menu) => {
 | 
			
		||||
                return (
 | 
			
		||||
                  <div>
 | 
			
		||||
                    {menu}
 | 
			
		||||
                    <Divider style={{ margin: '5px 0 4px' }} />
 | 
			
		||||
                    <div
 | 
			
		||||
                      style={{
 | 
			
		||||
                        cursor: 'pointer',
 | 
			
		||||
                        padding: '5px 12px',
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      <Input.Search
 | 
			
		||||
                        size={'middle'}
 | 
			
		||||
                        placeholder={'新增数据表'}
 | 
			
		||||
                        enterButton={<PlusOutlined />}
 | 
			
		||||
                        value={newValue}
 | 
			
		||||
                        onChange={(e) => {
 | 
			
		||||
                          setNewValue(e.target.value);
 | 
			
		||||
                        }}
 | 
			
		||||
                        onSearch={async (value) => {
 | 
			
		||||
                          const data = {
 | 
			
		||||
                            name: uid(),
 | 
			
		||||
                            title: value,
 | 
			
		||||
                          };
 | 
			
		||||
                          field.push(data);
 | 
			
		||||
                          setActiveIndex(field.value.length - 1);
 | 
			
		||||
                          setOpen(false);
 | 
			
		||||
                          setNewValue('');
 | 
			
		||||
                          await createOrUpdateCollection(data);
 | 
			
		||||
                        }}
 | 
			
		||||
                      />
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                );
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              {field.value?.map((item, index) => {
 | 
			
		||||
                return (
 | 
			
		||||
                  <Select.Option value={index} label={item.title || '未命名'}>
 | 
			
		||||
                    <div
 | 
			
		||||
                      style={{
 | 
			
		||||
                        display: 'flex',
 | 
			
		||||
                        justifyContent: 'space-between',
 | 
			
		||||
                        alignItems: 'center',
 | 
			
		||||
                      }}
 | 
			
		||||
                    >
 | 
			
		||||
                      {item.title || '未命名'}
 | 
			
		||||
                      <DeleteOutlined
 | 
			
		||||
                        onClick={async (e) => {
 | 
			
		||||
                          e.stopPropagation();
 | 
			
		||||
                          field.remove(index);
 | 
			
		||||
                          if (activeIndex === index) {
 | 
			
		||||
                            setActiveIndex(0);
 | 
			
		||||
                          } else if (activeIndex > index) {
 | 
			
		||||
                            setActiveIndex(activeIndex - 1);
 | 
			
		||||
                          }
 | 
			
		||||
                          await deleteCollection(item.name);
 | 
			
		||||
                        }}
 | 
			
		||||
                      />
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </Select.Option>
 | 
			
		||||
                );
 | 
			
		||||
              })}
 | 
			
		||||
            </Select>
 | 
			
		||||
          </div>
 | 
			
		||||
        }
 | 
			
		||||
        visible={visible}
 | 
			
		||||
        onCancel={() => {
 | 
			
		||||
          setVisible(false);
 | 
			
		||||
        }}
 | 
			
		||||
        onOk={async () => {
 | 
			
		||||
          try {
 | 
			
		||||
            form.clearErrors();
 | 
			
		||||
            await form.validate(`${field.address.entire}.${activeIndex}.*`);
 | 
			
		||||
            setVisible(false);
 | 
			
		||||
            await createOrUpdateCollection(field.value[activeIndex]);
 | 
			
		||||
            console.log(
 | 
			
		||||
              `${field.address.entire}.${activeIndex}.*`,
 | 
			
		||||
              field.value[activeIndex],
 | 
			
		||||
            );
 | 
			
		||||
          } catch (error) {}
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <FormLayout layout={'vertical'}>
 | 
			
		||||
          <RecursionField
 | 
			
		||||
            name={activeIndex}
 | 
			
		||||
            schema={
 | 
			
		||||
              new Schema({
 | 
			
		||||
                type: 'object',
 | 
			
		||||
                properties: schema.properties,
 | 
			
		||||
              })
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
        </FormLayout>
 | 
			
		||||
      </Modal>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const DatabaseField: any = observer((props) => {
 | 
			
		||||
  const field = useField<Formily.Core.Models.ArrayField>();
 | 
			
		||||
  console.log('DatabaseField', field.componentProps);
 | 
			
		||||
  console.log('DatabaseField', field.value);
 | 
			
		||||
  const [activeKey, setActiveKey] = useState(null);
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
@ -26,6 +186,7 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
        onChange={(key) => {
 | 
			
		||||
          setActiveKey(key);
 | 
			
		||||
        }}
 | 
			
		||||
        className={cls({ empty: !field.value?.length })}
 | 
			
		||||
        accordion
 | 
			
		||||
      >
 | 
			
		||||
        {field.value?.map((item, index) => {
 | 
			
		||||
@ -35,9 +196,13 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
            <Collapse.Panel
 | 
			
		||||
              header={
 | 
			
		||||
                <>
 | 
			
		||||
                  {(item.ui && item.ui.title) || <i style={{color: 'rgba(0, 0, 0, 0.25)'}}>未命名</i>}{' '}
 | 
			
		||||
                  {(item.ui && item.ui.title) || (
 | 
			
		||||
                    <i style={{ color: 'rgba(0, 0, 0, 0.25)' }}>未命名</i>
 | 
			
		||||
                  )}{' '}
 | 
			
		||||
                  <Tag>{schema.title}</Tag>
 | 
			
		||||
                  <span style={{ color: 'rgba(0, 0, 0, 0.25)', fontSize: 14 }}>{item.name}</span>
 | 
			
		||||
                  <span style={{ color: 'rgba(0, 0, 0, 0.25)', fontSize: 14 }}>
 | 
			
		||||
                    {item.name}
 | 
			
		||||
                  </span>
 | 
			
		||||
                </>
 | 
			
		||||
              }
 | 
			
		||||
              extra={[
 | 
			
		||||
@ -97,13 +262,11 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
              console.log('info.key', info.key, schema);
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            {options.map(option => (
 | 
			
		||||
            {options.map((option) => (
 | 
			
		||||
              <Menu.ItemGroup title={option.label}>
 | 
			
		||||
                {
 | 
			
		||||
                  option.children.map(item => (
 | 
			
		||||
                {option.children.map((item) => (
 | 
			
		||||
                  <Menu.Item key={item.name}>{item.title}</Menu.Item>
 | 
			
		||||
                  ))
 | 
			
		||||
                }
 | 
			
		||||
                ))}
 | 
			
		||||
              </Menu.ItemGroup>
 | 
			
		||||
            ))}
 | 
			
		||||
          </Menu>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								packages/client/src/schemas/database-field/style.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/client/src/schemas/database-field/style.less
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
.ant-collapse.empty {
 | 
			
		||||
  border: 0;
 | 
			
		||||
}
 | 
			
		||||
@ -21,7 +21,7 @@ import {
 | 
			
		||||
import './style.less';
 | 
			
		||||
import cls from 'classnames';
 | 
			
		||||
 | 
			
		||||
import { createSchema, useDesignable, useSchemaPath } from '../';
 | 
			
		||||
import { createSchema, removeSchema, useDesignable, useSchemaPath } from '../';
 | 
			
		||||
import {
 | 
			
		||||
  useDrag,
 | 
			
		||||
  useDrop,
 | 
			
		||||
@ -37,6 +37,10 @@ export const GridBlockContext = createContext({
 | 
			
		||||
  dragRef: null,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function isGridRowOrCol(schema: ISchema) {
 | 
			
		||||
  return ['Grid.Row', 'Grid.Col'].includes(schema['x-component']);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RowDivider = ({ name, onDrop }) => {
 | 
			
		||||
  const uid = useDragDropUID();
 | 
			
		||||
  const { isOver, dropRef } = useDrop({
 | 
			
		||||
@ -111,7 +115,11 @@ export const Grid: any = observer((props) => {
 | 
			
		||||
            });
 | 
			
		||||
            await createSchema(data);
 | 
			
		||||
            console.log('prepend', data);
 | 
			
		||||
            deepRemove(path);
 | 
			
		||||
            const removed = deepRemove(path);
 | 
			
		||||
            const last = removed.pop();
 | 
			
		||||
            if (isGridRowOrCol(last)) {
 | 
			
		||||
              await removeSchema(last);
 | 
			
		||||
            }
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
        {schema.mapProperties((property, key, index) => {
 | 
			
		||||
@ -143,8 +151,12 @@ export const Grid: any = observer((props) => {
 | 
			
		||||
                    [...gridPath, key],
 | 
			
		||||
                  );
 | 
			
		||||
                  await createSchema(data);
 | 
			
		||||
                  console.log('insertAfter', data);
 | 
			
		||||
                  deepRemove(path);
 | 
			
		||||
                  const removed = deepRemove(path);
 | 
			
		||||
                  const last = removed.pop();
 | 
			
		||||
                  if (isGridRowOrCol(last)) {
 | 
			
		||||
                    await removeSchema(last);
 | 
			
		||||
                  }
 | 
			
		||||
                  console.log('insertAfter', data, removed);
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
            </>
 | 
			
		||||
@ -185,7 +197,11 @@ Grid.Row = observer((props) => {
 | 
			
		||||
          });
 | 
			
		||||
          await createSchema(data);
 | 
			
		||||
          const path = [...e.dragItem.path];
 | 
			
		||||
          deepRemove(path);
 | 
			
		||||
          const removed = deepRemove(path);
 | 
			
		||||
          const last = removed.pop();
 | 
			
		||||
          if (isGridRowOrCol(last)) {
 | 
			
		||||
            await removeSchema(last);
 | 
			
		||||
          }
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
      {schema.mapProperties((property, key, index) => {
 | 
			
		||||
@ -207,10 +223,14 @@ Grid.Row = observer((props) => {
 | 
			
		||||
                  },
 | 
			
		||||
                  [...rowPath, key],
 | 
			
		||||
                );
 | 
			
		||||
                console.log('ColDivider', data)
 | 
			
		||||
                console.log('ColDivider', data);
 | 
			
		||||
                await createSchema(data);
 | 
			
		||||
                const path = [...e.dragItem.path];
 | 
			
		||||
                deepRemove(path);
 | 
			
		||||
                const removed = deepRemove(path);
 | 
			
		||||
                const last = removed.pop();
 | 
			
		||||
                if (isGridRowOrCol(last)) {
 | 
			
		||||
                  await removeSchema(last);
 | 
			
		||||
                }
 | 
			
		||||
              }}
 | 
			
		||||
              onDragEnd={(e) => {
 | 
			
		||||
                designableSchema.mapProperties((s, key, index) => {
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,23 @@ export const request = extend({
 | 
			
		||||
  timeout: 1000,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export async function createOrUpdateCollection(data: any) {
 | 
			
		||||
  return await request('collections:createOrUpdate', {
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    data,
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function deleteCollection(name) {
 | 
			
		||||
  await request('collections:destroy', {
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    params: {
 | 
			
		||||
      filter: {
 | 
			
		||||
        name,
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function createSchema(schema: ISchema) {
 | 
			
		||||
  if (!schema['key']) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								packages/plugin-collections/src/actions/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								packages/plugin-collections/src/actions/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
import { Model, ModelCtor } from '@nocobase/database';
 | 
			
		||||
import { actions, middlewares } from '@nocobase/actions';
 | 
			
		||||
import { sort } from '@nocobase/actions/src/actions/common';
 | 
			
		||||
import { cloneDeep, omit } from 'lodash';
 | 
			
		||||
 | 
			
		||||
export const createOrUpdate = async (ctx: actions.Context, next: actions.Next) => {
 | 
			
		||||
  const { values } = ctx.action.params;
 | 
			
		||||
  const Collection = ctx.db.getModel('collections');
 | 
			
		||||
  let collection;
 | 
			
		||||
  if (values.name) {
 | 
			
		||||
    collection = await Collection.findByPk(values.name);
 | 
			
		||||
  }
 | 
			
		||||
  try {
 | 
			
		||||
    if (!collection) {
 | 
			
		||||
      collection = await Collection.create(values);
 | 
			
		||||
    } else {
 | 
			
		||||
      await collection.update(values);
 | 
			
		||||
    }
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.log('error.errors', error.errors)
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  ctx.body = collection;
 | 
			
		||||
}
 | 
			
		||||
@ -2,6 +2,7 @@ import path from 'path';
 | 
			
		||||
import { Application } from '@nocobase/server';
 | 
			
		||||
import { registerModels, Table } from '@nocobase/database';
 | 
			
		||||
import * as models from './models';
 | 
			
		||||
import { createOrUpdate } from './actions';
 | 
			
		||||
 | 
			
		||||
export default async function (this: Application, options = {}) {
 | 
			
		||||
  const database = this.database;
 | 
			
		||||
@ -16,4 +17,6 @@ export default async function (this: Application, options = {}) {
 | 
			
		||||
      model.set('name', model.get('key'));
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  this.resourcer.registerActionHandler('collections:createOrUpdate', createOrUpdate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,14 +18,19 @@ export const create = async (ctx: actions.Context, next: actions.Next) => {
 | 
			
		||||
      payload: 'replace',
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
  await actions.common.create(ctx, async () => {});
 | 
			
		||||
  await actions.common.create(ctx, async () => { });
 | 
			
		||||
  const sticky = values['__prepend__'];
 | 
			
		||||
  const targetKey = values['__insertAfter__'] || values['__insertBefore__'];
 | 
			
		||||
  if (targetKey) {
 | 
			
		||||
  if (sticky || targetKey) {
 | 
			
		||||
    console.log({
 | 
			
		||||
      associatedKey: values.parentKey,
 | 
			
		||||
      resourceKey: ctx.body.key,
 | 
			
		||||
      values: {
 | 
			
		||||
      values: sticky ? {
 | 
			
		||||
        field: 'sort',
 | 
			
		||||
        sticky: true,
 | 
			
		||||
      } : {
 | 
			
		||||
        field: 'sort',
 | 
			
		||||
        insertAfter: !!values['__insertAfter__'],
 | 
			
		||||
        target: {
 | 
			
		||||
          key: targetKey,
 | 
			
		||||
        },
 | 
			
		||||
@ -35,7 +40,11 @@ export const create = async (ctx: actions.Context, next: actions.Next) => {
 | 
			
		||||
      {
 | 
			
		||||
        associatedKey: values.parentKey,
 | 
			
		||||
        resourceKey: ctx.body.key,
 | 
			
		||||
        values: {
 | 
			
		||||
        values: sticky ? {
 | 
			
		||||
          field: 'sort',
 | 
			
		||||
          sticky: true,
 | 
			
		||||
          target: {},
 | 
			
		||||
        } : {
 | 
			
		||||
          field: 'sort',
 | 
			
		||||
          insertAfter: !!values['__insertAfter__'],
 | 
			
		||||
          target: {
 | 
			
		||||
@ -47,8 +56,8 @@ export const create = async (ctx: actions.Context, next: actions.Next) => {
 | 
			
		||||
        payload: 'replace',
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
    await middlewares.associated(ctx, async () => {});
 | 
			
		||||
    await sort(ctx, async () => {});
 | 
			
		||||
    await middlewares.associated(ctx, async () => { });
 | 
			
		||||
    await sort(ctx, async () => { });
 | 
			
		||||
  }
 | 
			
		||||
  await next();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user