updates
This commit is contained in:
		
							parent
							
								
									e21a9ab57d
								
							
						
					
					
						commit
						3223d95d01
					
				@ -1,5 +1,6 @@
 | 
			
		||||
import { SchemaRenderer } from '../../';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { FormItem } from '@formily/antd';
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  const schema = {
 | 
			
		||||
@ -32,5 +33,5 @@ export default () => {
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
  return <SchemaRenderer schema={schema} />;
 | 
			
		||||
  return <SchemaRenderer components={{ FormItem }} schema={schema} />;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -69,10 +69,11 @@ export function createRouteSwitch(options: RouteSwitchOptions) {
 | 
			
		||||
        value={{ ...options.components, ...props.components }}
 | 
			
		||||
      >
 | 
			
		||||
        <Switch>
 | 
			
		||||
          {routes.map((route) => {
 | 
			
		||||
          {routes.map((route, index) => {
 | 
			
		||||
            if (route.type == 'redirect') {
 | 
			
		||||
              return (
 | 
			
		||||
                <Redirect
 | 
			
		||||
                  key={index}
 | 
			
		||||
                  to={route.to}
 | 
			
		||||
                  push={route.push}
 | 
			
		||||
                  from={route.from}
 | 
			
		||||
@ -87,6 +88,7 @@ export function createRouteSwitch(options: RouteSwitchOptions) {
 | 
			
		||||
            }
 | 
			
		||||
            return (
 | 
			
		||||
              <Route
 | 
			
		||||
                key={index}
 | 
			
		||||
                path={route.path}
 | 
			
		||||
                exact={route.exact}
 | 
			
		||||
                strict={route.strict}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import request from 'umi-request';
 | 
			
		||||
import { request } from './schemas';
 | 
			
		||||
 | 
			
		||||
export interface ResourceOptions {
 | 
			
		||||
  resourceName: string;
 | 
			
		||||
@ -33,7 +33,7 @@ export class Resource {
 | 
			
		||||
 | 
			
		||||
  list(options: ListOptions = {}) {
 | 
			
		||||
    const { resourceName } = this.options;
 | 
			
		||||
    return request(`/api/${resourceName}:list`);
 | 
			
		||||
    return request(`${resourceName}:list`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get(options: GetOptions = {}) {
 | 
			
		||||
@ -42,19 +42,30 @@ export class Resource {
 | 
			
		||||
    if (!resourceKey) {
 | 
			
		||||
      return Promise.resolve({ data: {} });
 | 
			
		||||
    }
 | 
			
		||||
    return request(`/api/${resourceName}:get/${resourceKey}`);
 | 
			
		||||
    return request(`${resourceName}:get/${resourceKey}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  save(values: any, options: SaveOptions = {}) {
 | 
			
		||||
    const resourceKey = options.resourceKey || this.options.resourceKey;
 | 
			
		||||
    const { resourceName } = this.options;
 | 
			
		||||
    const url = `/api/${resourceName}:${resourceKey ? `update/${resourceKey}` : 'create'}`;
 | 
			
		||||
    const url = `${resourceName}:${resourceKey ? `update/${resourceKey}` : 'create'}`;
 | 
			
		||||
    return request(url, {
 | 
			
		||||
      method: 'post',
 | 
			
		||||
      data: values,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  destroy(filter: any) {
 | 
			
		||||
    const { resourceName } = this.options;
 | 
			
		||||
    const url = `${resourceName}:destroy`;
 | 
			
		||||
    return request(url, {
 | 
			
		||||
      method: 'get',
 | 
			
		||||
      params: {
 | 
			
		||||
        filter
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static make(options: string | Resource | ResourceOptions) {
 | 
			
		||||
    if (typeof options === 'string') {
 | 
			
		||||
      return new Resource({ resourceName: options });
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ import {
 | 
			
		||||
} from '@formily/react';
 | 
			
		||||
import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
 | 
			
		||||
import { Link, useHistory, LinkProps } from 'react-router-dom';
 | 
			
		||||
import { useDesignable, VisibleContext, useDefaultAction } from '..';
 | 
			
		||||
import { useDesignable, VisibleContext, useDefaultAction, useVisible } from '..';
 | 
			
		||||
import './style.less';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
import cls from 'classnames';
 | 
			
		||||
@ -25,8 +25,9 @@ import { useSchemaComponent } from '../../components/schema-renderer';
 | 
			
		||||
export const Action: any = observer((props: any) => {
 | 
			
		||||
  const { useAction = useDefaultAction, icon, ...others } = props;
 | 
			
		||||
  const { run } = useAction();
 | 
			
		||||
  const field = useField();
 | 
			
		||||
  const { schema, DesignableBar } = useDesignable();
 | 
			
		||||
  const [visible, setVisible] = useState(false);
 | 
			
		||||
  const { visible, setVisible } = useVisible(field.address.entire);
 | 
			
		||||
  const child = Object.values(schema.properties || {}).shift();
 | 
			
		||||
  const isDropdownOrPopover =
 | 
			
		||||
    child &&
 | 
			
		||||
@ -74,6 +75,7 @@ Action.Modal = observer((props: any) => {
 | 
			
		||||
  } = props;
 | 
			
		||||
  const { schema } = useDesignable();
 | 
			
		||||
  const [visible, setVisible] = useContext(VisibleContext);
 | 
			
		||||
  console.log('VisibleContext', visible);
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  const { run: runOk } = useOkAction();
 | 
			
		||||
  const { run: runCancel } = useCancelAction();
 | 
			
		||||
@ -81,7 +83,7 @@ Action.Modal = observer((props: any) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <Modal
 | 
			
		||||
      title={schema.title}
 | 
			
		||||
      destroyOnClose
 | 
			
		||||
      // destroyOnClose
 | 
			
		||||
      maskClosable
 | 
			
		||||
      footer={
 | 
			
		||||
        isFormDecorator
 | 
			
		||||
 | 
			
		||||
@ -64,6 +64,8 @@ import { cloneDeep } from 'lodash';
 | 
			
		||||
import { options } from '../database-field/interfaces';
 | 
			
		||||
import { useDisplayFieldsContext } from '../form';
 | 
			
		||||
 | 
			
		||||
import './style.less';
 | 
			
		||||
 | 
			
		||||
const generateGridBlock = (schema: ISchema) => {
 | 
			
		||||
  const name = schema.name || uid();
 | 
			
		||||
  return {
 | 
			
		||||
@ -117,17 +119,17 @@ function generateCardItemSchema(component) {
 | 
			
		||||
      'x-decorator': 'CardItem',
 | 
			
		||||
      'x-component': 'Table',
 | 
			
		||||
      default: [
 | 
			
		||||
        { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        // { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        // { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        // { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        // { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        // { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        // { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        // { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
        // { key: uid(), field1: uid(), field2: uid() },
 | 
			
		||||
      ],
 | 
			
		||||
      'x-component-props': {
 | 
			
		||||
        rowKey: 'key',
 | 
			
		||||
        rowKey: 'id',
 | 
			
		||||
        dragSort: true,
 | 
			
		||||
        showIndex: true,
 | 
			
		||||
      },
 | 
			
		||||
@ -232,45 +234,34 @@ function generateCardItemSchema(component) {
 | 
			
		||||
                  'x-component': 'Menu.Action',
 | 
			
		||||
                  'x-designable-bar': 'Table.Action.DesignableBar',
 | 
			
		||||
                  properties: {
 | 
			
		||||
                    drawer1: {
 | 
			
		||||
                    [uid()]: {
 | 
			
		||||
                      type: 'void',
 | 
			
		||||
                      title: '查看',
 | 
			
		||||
                      'x-component': 'Action.Modal',
 | 
			
		||||
                      'x-component-props': {},
 | 
			
		||||
                      'x-component-props': {
 | 
			
		||||
                        bodyStyle: {
 | 
			
		||||
                          background: '#f0f2f5',
 | 
			
		||||
                          paddingTop: 0,
 | 
			
		||||
                        },
 | 
			
		||||
                      },
 | 
			
		||||
                      properties: {
 | 
			
		||||
                        [uid()]: {
 | 
			
		||||
                          type: 'void',
 | 
			
		||||
                          'x-component': 'Tabs',
 | 
			
		||||
                          properties: {
 | 
			
		||||
                            tab1: {
 | 
			
		||||
                            [uid()]: {
 | 
			
		||||
                              type: 'void',
 | 
			
		||||
                              'x-component': 'Tabs.TabPane',
 | 
			
		||||
                              'x-component-props': {
 | 
			
		||||
                                tab: 'Tab1',
 | 
			
		||||
                              },
 | 
			
		||||
                              properties: {
 | 
			
		||||
                                aaa: {
 | 
			
		||||
                                  type: 'string',
 | 
			
		||||
                                  title: 'AAA',
 | 
			
		||||
                                  'x-decorator': 'FormItem',
 | 
			
		||||
                                  required: true,
 | 
			
		||||
                                  'x-component': 'Input',
 | 
			
		||||
                                },
 | 
			
		||||
                              },
 | 
			
		||||
                            },
 | 
			
		||||
                            tab2: {
 | 
			
		||||
                              type: 'void',
 | 
			
		||||
                              'x-component': 'Tabs.TabPane',
 | 
			
		||||
                              'x-component-props': {
 | 
			
		||||
                                tab: 'Tab2',
 | 
			
		||||
                              },
 | 
			
		||||
                              properties: {
 | 
			
		||||
                                bbb: {
 | 
			
		||||
                                  type: 'string',
 | 
			
		||||
                                  title: 'BBB',
 | 
			
		||||
                                  'x-decorator': 'FormItem',
 | 
			
		||||
                                  required: true,
 | 
			
		||||
                                  'x-component': 'Input',
 | 
			
		||||
                                [uid()]: {
 | 
			
		||||
                                  type: 'void',
 | 
			
		||||
                                  'x-component': 'Grid',
 | 
			
		||||
                                  'x-component-props': {
 | 
			
		||||
                                    addNewComponent: 'AddNew.PaneItem',
 | 
			
		||||
                                  },
 | 
			
		||||
                                },
 | 
			
		||||
                              },
 | 
			
		||||
                            },
 | 
			
		||||
@ -282,14 +273,13 @@ function generateCardItemSchema(component) {
 | 
			
		||||
                },
 | 
			
		||||
                [uid()]: {
 | 
			
		||||
                  type: 'void',
 | 
			
		||||
                  name: 'action1',
 | 
			
		||||
                  title: '修改',
 | 
			
		||||
                  title: '编辑',
 | 
			
		||||
                  'x-component': 'Menu.Action',
 | 
			
		||||
                  'x-designable-bar': 'Table.Action.DesignableBar',
 | 
			
		||||
                  properties: {
 | 
			
		||||
                    drawer1: {
 | 
			
		||||
                    [uid()]: {
 | 
			
		||||
                      type: 'void',
 | 
			
		||||
                      title: '编辑表单',
 | 
			
		||||
                      title: '编辑数据',
 | 
			
		||||
                      'x-decorator': 'Form',
 | 
			
		||||
                      'x-decorator-props': {
 | 
			
		||||
                        useValues: '{{ Table.useTableRow }}',
 | 
			
		||||
@ -299,17 +289,12 @@ function generateCardItemSchema(component) {
 | 
			
		||||
                        useOkAction: '{{ Table.useTableUpdateAction }}',
 | 
			
		||||
                      },
 | 
			
		||||
                      properties: {
 | 
			
		||||
                        field1: {
 | 
			
		||||
                          type: 'string',
 | 
			
		||||
                          title: '字段1',
 | 
			
		||||
                          'x-decorator': 'FormItem',
 | 
			
		||||
                          'x-component': 'Input',
 | 
			
		||||
                        },
 | 
			
		||||
                        field2: {
 | 
			
		||||
                          type: 'string',
 | 
			
		||||
                          title: '字段2',
 | 
			
		||||
                          'x-decorator': 'FormItem',
 | 
			
		||||
                          'x-component': 'Input',
 | 
			
		||||
                        [uid()]: {
 | 
			
		||||
                          type: 'void',
 | 
			
		||||
                          'x-component': 'Grid',
 | 
			
		||||
                          'x-component-props': {
 | 
			
		||||
                            addNewComponent: 'AddNew.FormItem',
 | 
			
		||||
                          },
 | 
			
		||||
                        },
 | 
			
		||||
                      },
 | 
			
		||||
                    },
 | 
			
		||||
@ -327,48 +312,6 @@ function generateCardItemSchema(component) {
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        [uid()]: {
 | 
			
		||||
          type: 'void',
 | 
			
		||||
          title: '字段1',
 | 
			
		||||
          'x-component': 'Table.Column',
 | 
			
		||||
          'x-component-props': {
 | 
			
		||||
            // title: 'z1',
 | 
			
		||||
          },
 | 
			
		||||
          'x-designable-bar': 'Table.Column.DesignableBar',
 | 
			
		||||
          properties: {
 | 
			
		||||
            field1: {
 | 
			
		||||
              type: 'string',
 | 
			
		||||
              required: true,
 | 
			
		||||
              'x-read-pretty': true,
 | 
			
		||||
              'x-decorator-props': {
 | 
			
		||||
                feedbackLayout: 'popover',
 | 
			
		||||
              },
 | 
			
		||||
              'x-decorator': 'FormItem',
 | 
			
		||||
              'x-component': 'Input',
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        [uid()]: {
 | 
			
		||||
          type: 'void',
 | 
			
		||||
          title: '字段2',
 | 
			
		||||
          'x-component': 'Table.Column',
 | 
			
		||||
          'x-component-props': {
 | 
			
		||||
            // title: 'z1',
 | 
			
		||||
          },
 | 
			
		||||
          'x-designable-bar': 'Table.Column.DesignableBar',
 | 
			
		||||
          properties: {
 | 
			
		||||
            field2: {
 | 
			
		||||
              type: 'string',
 | 
			
		||||
              required: true,
 | 
			
		||||
              'x-read-pretty': true,
 | 
			
		||||
              'x-decorator-props': {
 | 
			
		||||
                feedbackLayout: 'popover',
 | 
			
		||||
              },
 | 
			
		||||
              'x-decorator': 'FormItem',
 | 
			
		||||
              'x-component': 'Input',
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    Form: {
 | 
			
		||||
@ -679,7 +622,7 @@ AddNew.FormItem = observer((props: any) => {
 | 
			
		||||
      placement={'bottomCenter'}
 | 
			
		||||
      overlay={
 | 
			
		||||
        <Menu>
 | 
			
		||||
          <Menu.ItemGroup title={`要展示的字段`}>
 | 
			
		||||
          <Menu.ItemGroup className={'display-fields'} title={`要展示的字段`}>
 | 
			
		||||
            {resource?.fields?.map((field) => (
 | 
			
		||||
              <Menu.Item key={field.key}>
 | 
			
		||||
                <FieldSwitch
 | 
			
		||||
@ -723,7 +666,11 @@ AddNew.FormItem = observer((props: any) => {
 | 
			
		||||
            ))}
 | 
			
		||||
          </Menu.ItemGroup>
 | 
			
		||||
          <Menu.Divider />
 | 
			
		||||
          <Menu.SubMenu title={'新建字段'}>
 | 
			
		||||
          <Menu.SubMenu
 | 
			
		||||
            popupClassName={'add-new-fields-popup'}
 | 
			
		||||
            className={'sub-menu-add-new-fields'}
 | 
			
		||||
            title={'新建字段'}
 | 
			
		||||
          >
 | 
			
		||||
            {options.map(
 | 
			
		||||
              (option) =>
 | 
			
		||||
                option.children.length > 0 && (
 | 
			
		||||
@ -827,4 +774,163 @@ AddNew.FormItem = observer((props: any) => {
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
AddNew.PaneItem = observer((props: any) => {
 | 
			
		||||
  const { ghost, defaultAction } = props;
 | 
			
		||||
  const { schema, insertBefore, insertAfter, appendChild } = useDesignable();
 | 
			
		||||
  const path = useSchemaPath();
 | 
			
		||||
  const { resource = {}, refresh } = useResourceContext();
 | 
			
		||||
  const [visible, setVisible] = useState(false);
 | 
			
		||||
  const { displayFields = [] } = useDisplayFieldsContext();
 | 
			
		||||
  console.log({ displayFields });
 | 
			
		||||
  return (
 | 
			
		||||
    <Dropdown
 | 
			
		||||
      trigger={['click']}
 | 
			
		||||
      visible={visible}
 | 
			
		||||
      onVisibleChange={setVisible}
 | 
			
		||||
      overlayStyle={{
 | 
			
		||||
        minWidth: 200,
 | 
			
		||||
      }}
 | 
			
		||||
      placement={'bottomCenter'}
 | 
			
		||||
      overlay={
 | 
			
		||||
        <Menu>
 | 
			
		||||
          <Menu.SubMenu title={'新建卡片'}>
 | 
			
		||||
            <Menu.Item
 | 
			
		||||
              onClick={async () => {
 | 
			
		||||
                let data: ISchema = {
 | 
			
		||||
                  type: 'void',
 | 
			
		||||
                  name: uid(),
 | 
			
		||||
                  'x-decorator': 'CardItem',
 | 
			
		||||
                  'x-component': 'Form',
 | 
			
		||||
                  'x-read-pretty': true,
 | 
			
		||||
                  'x-component-props': {},
 | 
			
		||||
                  'x-designable-bar': 'Form.DesignableBar',
 | 
			
		||||
                  properties: {
 | 
			
		||||
                    [uid()]: {
 | 
			
		||||
                      type: 'void',
 | 
			
		||||
                      'x-component': 'Grid',
 | 
			
		||||
                      'x-component-props': {
 | 
			
		||||
                        addNewComponent: 'AddNew.FormItem',
 | 
			
		||||
                      },
 | 
			
		||||
                    },
 | 
			
		||||
                  },
 | 
			
		||||
                };
 | 
			
		||||
                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);
 | 
			
		||||
                }
 | 
			
		||||
                setVisible(false);
 | 
			
		||||
              }}
 | 
			
		||||
              style={{ minWidth: 150 }}
 | 
			
		||||
            >
 | 
			
		||||
              阅读模式
 | 
			
		||||
            </Menu.Item>
 | 
			
		||||
            <Menu.Item
 | 
			
		||||
              onClick={async () => {
 | 
			
		||||
                let data: ISchema = {
 | 
			
		||||
                  type: 'void',
 | 
			
		||||
                  name: uid(),
 | 
			
		||||
                  'x-decorator': 'CardItem',
 | 
			
		||||
                  'x-component': 'Form',
 | 
			
		||||
                  'x-component-props': {
 | 
			
		||||
                    showDefaultButtons: true,
 | 
			
		||||
                  },
 | 
			
		||||
                  'x-designable-bar': 'Form.DesignableBar',
 | 
			
		||||
                  properties: {
 | 
			
		||||
                    [uid()]: {
 | 
			
		||||
                      type: 'void',
 | 
			
		||||
                      'x-component': 'Grid',
 | 
			
		||||
                      'x-component-props': {
 | 
			
		||||
                        addNewComponent: 'AddNew.FormItem',
 | 
			
		||||
                      },
 | 
			
		||||
                    },
 | 
			
		||||
                  },
 | 
			
		||||
                };
 | 
			
		||||
                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);
 | 
			
		||||
                }
 | 
			
		||||
                setVisible(false);
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              编辑模式
 | 
			
		||||
            </Menu.Item>
 | 
			
		||||
          </Menu.SubMenu>
 | 
			
		||||
          <Menu.SubMenu title={'要展示的相关数据'}>
 | 
			
		||||
            <Menu.Item style={{ minWidth: 150 }}>日志</Menu.Item>
 | 
			
		||||
            <Menu.Item>评论</Menu.Item>
 | 
			
		||||
          </Menu.SubMenu>
 | 
			
		||||
          <Menu.Item
 | 
			
		||||
            onClick={async () => {
 | 
			
		||||
              let data: ISchema = {
 | 
			
		||||
                key: uid(),
 | 
			
		||||
                type: 'void',
 | 
			
		||||
                default: '这是一段演示文字,**支持使用 Markdown 语法**',
 | 
			
		||||
                'x-designable-bar': 'Markdown.Void.DesignableBar',
 | 
			
		||||
                'x-decorator': 'CardItem',
 | 
			
		||||
                'x-read-pretty': true,
 | 
			
		||||
                'x-component': 'Markdown.Void',
 | 
			
		||||
              };
 | 
			
		||||
              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);
 | 
			
		||||
              }
 | 
			
		||||
              setVisible(false);
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            添加说明文字
 | 
			
		||||
          </Menu.Item>
 | 
			
		||||
        </Menu>
 | 
			
		||||
      }
 | 
			
		||||
    >
 | 
			
		||||
      {ghost ? (
 | 
			
		||||
        <PlusOutlined />
 | 
			
		||||
      ) : (
 | 
			
		||||
        <Button block type={'dashed'} icon={<PlusOutlined />}></Button>
 | 
			
		||||
      )}
 | 
			
		||||
    </Dropdown>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export default AddNew;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								packages/client/src/schemas/add-new/style.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								packages/client/src/schemas/add-new/style.less
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
.ant-dropdown-menu-item-group.display-fields {
 | 
			
		||||
  .ant-dropdown-menu-item-group-list {
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
    max-height: 300px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.add-new-fields-popup .ant-dropdown-menu {
 | 
			
		||||
  overflow: auto;
 | 
			
		||||
  max-height: 300px;
 | 
			
		||||
}
 | 
			
		||||
@ -145,6 +145,7 @@ export const DatabaseCollection = observer((props) => {
 | 
			
		||||
              {field.value?.map((item, index) => {
 | 
			
		||||
                return (
 | 
			
		||||
                  <Select.Option
 | 
			
		||||
                    key={index}
 | 
			
		||||
                    value={index}
 | 
			
		||||
                    label={`${item.title || '未命名'}${
 | 
			
		||||
                      item.unsaved ? ' (未保存)' : ''
 | 
			
		||||
@ -254,6 +255,7 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
            type: 'error',
 | 
			
		||||
            address: `*(${path},${path}.*)`,
 | 
			
		||||
          });
 | 
			
		||||
          console.log('item.key', item.key);
 | 
			
		||||
          return (
 | 
			
		||||
            <Collapse.Panel
 | 
			
		||||
              header={
 | 
			
		||||
@ -268,8 +270,9 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
                </>
 | 
			
		||||
              }
 | 
			
		||||
              extra={[
 | 
			
		||||
                <Badge count={errors.length} />,
 | 
			
		||||
                <Badge key={'1'} count={errors.length} />,
 | 
			
		||||
                <DeleteOutlined
 | 
			
		||||
                  key={'2'} 
 | 
			
		||||
                  onClick={(e) => {
 | 
			
		||||
                    e.stopPropagation();
 | 
			
		||||
                    field.remove(index);
 | 
			
		||||
@ -305,6 +308,7 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
      </Collapse>
 | 
			
		||||
      <Dropdown
 | 
			
		||||
        placement={'bottomCenter'}
 | 
			
		||||
        overlayClassName={'all-fields'}
 | 
			
		||||
        overlayStyle={{
 | 
			
		||||
          minWidth: 200,
 | 
			
		||||
        }}
 | 
			
		||||
@ -327,9 +331,9 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            {options.map(
 | 
			
		||||
              (option) =>
 | 
			
		||||
              (option, groupIndex) =>
 | 
			
		||||
                option.children.length > 0 && (
 | 
			
		||||
                  <Menu.ItemGroup title={option.label}>
 | 
			
		||||
                  <Menu.ItemGroup key={groupIndex} title={option.label}>
 | 
			
		||||
                    {option.children.map((item) => (
 | 
			
		||||
                      <Menu.Item key={item.name}>{item.title}</Menu.Item>
 | 
			
		||||
                    ))}
 | 
			
		||||
 | 
			
		||||
@ -14,3 +14,10 @@
 | 
			
		||||
    background: #1890ff;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ant-dropdown.all-fields {
 | 
			
		||||
  .ant-dropdown-menu {
 | 
			
		||||
    max-height: 300px;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ import {
 | 
			
		||||
  ResourceContextProvider,
 | 
			
		||||
} from '../';
 | 
			
		||||
import get from 'lodash/get';
 | 
			
		||||
import { Button, Dropdown, Menu, Space } from 'antd';
 | 
			
		||||
import { Button, Dropdown, Menu, message, Space } from 'antd';
 | 
			
		||||
import { MenuOutlined, DragOutlined } from '@ant-design/icons';
 | 
			
		||||
import cls from 'classnames';
 | 
			
		||||
import { FormLayout } from '@formily/antd';
 | 
			
		||||
@ -38,6 +38,7 @@ import { DesignableBar } from './DesignableBar';
 | 
			
		||||
import { FieldDesignableBar } from './Field.DesignableBar';
 | 
			
		||||
import { createContext } from 'react';
 | 
			
		||||
import { BlockItem } from '../block-item';
 | 
			
		||||
import { Resource } from '../../resource';
 | 
			
		||||
 | 
			
		||||
const [DisplayFieldsContextProvider, useDisplayFieldsContext] = constate(() => {
 | 
			
		||||
  const [displayFields, setDisplayFields] = useState([]);
 | 
			
		||||
@ -46,12 +47,12 @@ const [DisplayFieldsContextProvider, useDisplayFieldsContext] = constate(() => {
 | 
			
		||||
    displayFields,
 | 
			
		||||
    setDisplayFields,
 | 
			
		||||
    addDisplayField(fieldName) {
 | 
			
		||||
      setDisplayFields(fields => fields.concat(fieldName));
 | 
			
		||||
      setDisplayFields((fields) => fields.concat(fieldName));
 | 
			
		||||
    },
 | 
			
		||||
  }
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export { DisplayFieldsContextProvider, useDisplayFieldsContext }
 | 
			
		||||
export { DisplayFieldsContextProvider, useDisplayFieldsContext };
 | 
			
		||||
 | 
			
		||||
export const Form: any = observer((props: any) => {
 | 
			
		||||
  const {
 | 
			
		||||
@ -60,12 +61,12 @@ export const Form: any = observer((props: any) => {
 | 
			
		||||
    resourceName,
 | 
			
		||||
    ...others
 | 
			
		||||
  } = props;
 | 
			
		||||
  const { schema } = useDesignable();
 | 
			
		||||
  const initialValues = useValues();
 | 
			
		||||
  const form = useMemo(() => {
 | 
			
		||||
    console.log('Form.useMemo', initialValues);
 | 
			
		||||
    return createForm({ initialValues });
 | 
			
		||||
    return createForm({ initialValues, readPretty: schema['x-read-pretty'] });
 | 
			
		||||
  }, []);
 | 
			
		||||
  const { schema } = useDesignable();
 | 
			
		||||
  const path = useSchemaPath();
 | 
			
		||||
  const scope = useContext(SchemaExpressionScopeContext);
 | 
			
		||||
  const content = (
 | 
			
		||||
@ -102,6 +103,9 @@ export const Form: any = observer((props: any) => {
 | 
			
		||||
            onClick={async () => {
 | 
			
		||||
              const values = await form.submit();
 | 
			
		||||
              console.log({ values });
 | 
			
		||||
              await Resource.make(resourceName).save(values);
 | 
			
		||||
              message.success('保存成功');
 | 
			
		||||
              await form.reset();
 | 
			
		||||
            }}
 | 
			
		||||
            type={'primary'}
 | 
			
		||||
          >
 | 
			
		||||
@ -121,9 +125,7 @@ export const Form: any = observer((props: any) => {
 | 
			
		||||
  return resourceName ? (
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    <ResourceContextProvider resourceName={resourceName}>
 | 
			
		||||
      <DisplayFieldsContextProvider>
 | 
			
		||||
        {content}
 | 
			
		||||
      </DisplayFieldsContextProvider>
 | 
			
		||||
      <DisplayFieldsContextProvider>{content}</DisplayFieldsContextProvider>
 | 
			
		||||
    </ResourceContextProvider>
 | 
			
		||||
  ) : (
 | 
			
		||||
    content
 | 
			
		||||
@ -141,7 +143,7 @@ Form.Field = observer((props: any) => {
 | 
			
		||||
  const { resource = {} } = useResourceContext();
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (fieldName) {
 | 
			
		||||
      addDisplayField(fieldName);
 | 
			
		||||
      addDisplayField && addDisplayField(fieldName);
 | 
			
		||||
    }
 | 
			
		||||
  }, [fieldName]);
 | 
			
		||||
  if (!fieldName) {
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,21 @@ export function mapReadPretty(component: any, readPrettyProps?: any) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const VisibleContext = createContext(null);
 | 
			
		||||
 | 
			
		||||
export function useVisible(name, defaultValue = false) {
 | 
			
		||||
  const [visible, setVisible] = useState(defaultValue);
 | 
			
		||||
  return { visible, setVisible };
 | 
			
		||||
  // const [visible, setVisible] = useCookieState(`${name}-visible`, {
 | 
			
		||||
  //   defaultValue: defaultValue ? 'true' : null,
 | 
			
		||||
  // });
 | 
			
		||||
  // return {
 | 
			
		||||
  //   visible: visible === 'true' ? true : false,
 | 
			
		||||
  //   setVisible: (value) => {
 | 
			
		||||
  //     setVisible(value ? 'true' : null);
 | 
			
		||||
  //   },
 | 
			
		||||
  // }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const DesignableBarContext = createContext(null);
 | 
			
		||||
 | 
			
		||||
const [PageTitleContextProvider, usePageTitleContext] = constate(() => {
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ type ComposedInput = React.FC<InputProps> & {
 | 
			
		||||
export const Input: ComposedInput = connect(
 | 
			
		||||
  AntdInput,
 | 
			
		||||
  mapProps((props, field) => {
 | 
			
		||||
    console.log('fieldfieldfieldfieldfield', field.address.entire);
 | 
			
		||||
    return {
 | 
			
		||||
      ...props,
 | 
			
		||||
      suffix: (
 | 
			
		||||
 | 
			
		||||
@ -55,6 +55,7 @@ import {
 | 
			
		||||
  removeSchema,
 | 
			
		||||
  updateSchema,
 | 
			
		||||
  useDefaultAction,
 | 
			
		||||
  useVisible,
 | 
			
		||||
  VisibleContext,
 | 
			
		||||
} from '..';
 | 
			
		||||
import { useMount } from 'ahooks';
 | 
			
		||||
@ -205,7 +206,8 @@ Menu.Link = observer((props: any) => {
 | 
			
		||||
      eventKey={schema.name}
 | 
			
		||||
      key={schema.name}
 | 
			
		||||
    >
 | 
			
		||||
      <Link to={props.to}>{schema.title}</Link>
 | 
			
		||||
      {schema.title}
 | 
			
		||||
      {/* <Link to={props.to || schema.name}>{schema.title}</Link> */}
 | 
			
		||||
      <DesignableBar />
 | 
			
		||||
    </AntdMenu.Item>
 | 
			
		||||
  );
 | 
			
		||||
@ -232,7 +234,8 @@ Menu.URL = observer((props: any) => {
 | 
			
		||||
Menu.Action = observer((props: any) => {
 | 
			
		||||
  const { icon, useAction = useDefaultAction, ...others } = props;
 | 
			
		||||
  const { schema, DesignableBar } = useDesignable();
 | 
			
		||||
  const [visible, setVisible] = useState(false);
 | 
			
		||||
  const field = useField();
 | 
			
		||||
  const { visible, setVisible } = useVisible(field.address.entire);
 | 
			
		||||
  const { run } = useAction();
 | 
			
		||||
  return (
 | 
			
		||||
    <VisibleContext.Provider value={[visible, setVisible]}>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,10 @@
 | 
			
		||||
import React, { createContext, useContext, useRef, useState } from 'react';
 | 
			
		||||
import React, {
 | 
			
		||||
  createContext,
 | 
			
		||||
  useContext,
 | 
			
		||||
  useEffect,
 | 
			
		||||
  useRef,
 | 
			
		||||
  useState,
 | 
			
		||||
} from 'react';
 | 
			
		||||
import {
 | 
			
		||||
  useFieldSchema,
 | 
			
		||||
  Schema,
 | 
			
		||||
@ -22,7 +28,7 @@ import {
 | 
			
		||||
  Select,
 | 
			
		||||
  Switch,
 | 
			
		||||
} from 'antd';
 | 
			
		||||
import { findIndex, get, set } from 'lodash';
 | 
			
		||||
import { cloneDeep, findIndex, get, set } from 'lodash';
 | 
			
		||||
import constate from 'constate';
 | 
			
		||||
import useRequest from '@ahooksjs/use-request';
 | 
			
		||||
import { uid, clone } from '@formily/shared';
 | 
			
		||||
@ -39,9 +45,11 @@ import {
 | 
			
		||||
} from 'react-sortable-hoc';
 | 
			
		||||
import cls from 'classnames';
 | 
			
		||||
import {
 | 
			
		||||
  createSchema,
 | 
			
		||||
  getSchemaPath,
 | 
			
		||||
  removeSchema,
 | 
			
		||||
  ResourceContextProvider,
 | 
			
		||||
  updateSchema,
 | 
			
		||||
  useCollectionContext,
 | 
			
		||||
  useDesignable,
 | 
			
		||||
  useResourceContext,
 | 
			
		||||
@ -53,6 +61,8 @@ import { DraggableBlockContext } from '../../components/drag-and-drop';
 | 
			
		||||
import AddNew from '../add-new';
 | 
			
		||||
import { isGridRowOrCol } from '../grid';
 | 
			
		||||
import { options } from '../database-field/interfaces';
 | 
			
		||||
import { Resource } from '../../resource';
 | 
			
		||||
import { useDisplayFieldsContext } from '../form';
 | 
			
		||||
 | 
			
		||||
interface TableRowProps {
 | 
			
		||||
  index: number;
 | 
			
		||||
@ -131,10 +141,13 @@ function useTableActionBars() {
 | 
			
		||||
  return findActionBars(schema);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ResourceFieldContext = createContext(null);
 | 
			
		||||
 | 
			
		||||
function useTableColumns(props?: any) {
 | 
			
		||||
  const { schema, designable } = useDesignable();
 | 
			
		||||
  // const schema = useFieldSchema();
 | 
			
		||||
  const { dataSource } = props || {};
 | 
			
		||||
  const { dataSource, rowKey = 'id' } = props || {};
 | 
			
		||||
  const { resource = {} } = useResourceContext();
 | 
			
		||||
 | 
			
		||||
  function findColumns(schema: Schema): Schema[] {
 | 
			
		||||
    return schema.reduceProperties((columns, current) => {
 | 
			
		||||
@ -148,21 +161,35 @@ function useTableColumns(props?: any) {
 | 
			
		||||
  const columns = findColumns(schema).map((item) => {
 | 
			
		||||
    const columnProps = item['x-component-props'] || {};
 | 
			
		||||
    console.log(item);
 | 
			
		||||
    const resourceField = columnProps.fieldName
 | 
			
		||||
      ? resource?.fields?.find((item) => item.name === columnProps.fieldName)
 | 
			
		||||
      : null;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      title: <RecursionField name={item.name} schema={item} onlyRenderSelf />,
 | 
			
		||||
      title: (
 | 
			
		||||
        <ResourceFieldContext.Provider value={resourceField}>
 | 
			
		||||
          <RecursionField name={item.name} schema={item} onlyRenderSelf />
 | 
			
		||||
        </ResourceFieldContext.Provider>
 | 
			
		||||
      ),
 | 
			
		||||
      dataIndex: item.name,
 | 
			
		||||
      ...columnProps,
 | 
			
		||||
      render(value, record, recordIndex) {
 | 
			
		||||
        const index = dataSource.indexOf(record);
 | 
			
		||||
        const index = findIndex(
 | 
			
		||||
          dataSource,
 | 
			
		||||
          (item) => item[rowKey] === record[rowKey],
 | 
			
		||||
        );
 | 
			
		||||
        console.log({ dataSource, record, index });
 | 
			
		||||
        return (
 | 
			
		||||
          <TableRowContext.Provider
 | 
			
		||||
            value={{
 | 
			
		||||
              index: index,
 | 
			
		||||
              data: record,
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <RecursionField schema={item} name={index} onlyRenderProperties />
 | 
			
		||||
          </TableRowContext.Provider>
 | 
			
		||||
          <ResourceFieldContext.Provider value={resourceField}>
 | 
			
		||||
            <TableRowContext.Provider
 | 
			
		||||
              value={{
 | 
			
		||||
                index: index,
 | 
			
		||||
                data: record,
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Table.Cell schema={item} />
 | 
			
		||||
            </TableRowContext.Provider>
 | 
			
		||||
          </ResourceFieldContext.Provider>
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
@ -198,6 +225,7 @@ function useTableColumns(props?: any) {
 | 
			
		||||
 | 
			
		||||
function AddColumn() {
 | 
			
		||||
  const [visible, setVisible] = useState(false);
 | 
			
		||||
  const { appendChild } = useDesignable();
 | 
			
		||||
  const { resource = {}, refresh } = useResourceContext();
 | 
			
		||||
  return (
 | 
			
		||||
    <Dropdown
 | 
			
		||||
@ -206,7 +234,7 @@ function AddColumn() {
 | 
			
		||||
      onVisibleChange={setVisible}
 | 
			
		||||
      overlay={
 | 
			
		||||
        <Menu>
 | 
			
		||||
          <Menu.ItemGroup title={'要展示的字段'}>
 | 
			
		||||
          <Menu.ItemGroup className={'display-fields'} title={'要展示的字段'}>
 | 
			
		||||
            {resource?.fields?.map((field) => (
 | 
			
		||||
              <Menu.Item style={{ minWidth: 150 }}>
 | 
			
		||||
                <div
 | 
			
		||||
@ -215,15 +243,29 @@ function AddColumn() {
 | 
			
		||||
                    alignItems: 'center',
 | 
			
		||||
                    justifyContent: 'space-between',
 | 
			
		||||
                  }}
 | 
			
		||||
                  onClick={async () => {
 | 
			
		||||
                    const data = appendChild({
 | 
			
		||||
                      type: 'void',
 | 
			
		||||
                      'x-component': 'Table.Column',
 | 
			
		||||
                      'x-component-props': {
 | 
			
		||||
                        fieldName: field.name,
 | 
			
		||||
                      },
 | 
			
		||||
                      'x-designable-bar': 'Table.Column.DesignableBar',
 | 
			
		||||
                    });
 | 
			
		||||
                    await createSchema(data);
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  <span>{field.uiSchema.title}</span>
 | 
			
		||||
                  <Switch size={'small'} defaultChecked />
 | 
			
		||||
                  <Switch size={'small'} />
 | 
			
		||||
                </div>
 | 
			
		||||
              </Menu.Item>
 | 
			
		||||
            ))}
 | 
			
		||||
          </Menu.ItemGroup>
 | 
			
		||||
          <Menu.Divider />
 | 
			
		||||
          <Menu.SubMenu title={'新增字段'}>
 | 
			
		||||
          <Menu.SubMenu
 | 
			
		||||
            popupClassName={'add-new-fields-popup'}
 | 
			
		||||
            title={'新增字段'}
 | 
			
		||||
          >
 | 
			
		||||
            {options.map((option) => (
 | 
			
		||||
              <Menu.ItemGroup title={option.label}>
 | 
			
		||||
                {option.children.map((item) => (
 | 
			
		||||
@ -266,26 +308,31 @@ export function useTableIndex() {
 | 
			
		||||
 | 
			
		||||
export function useTableDestroyAction() {
 | 
			
		||||
  const ctx = useContext(TableRowContext);
 | 
			
		||||
  const { field, selectedRowKeys, setSelectedRowKeys, refresh } =
 | 
			
		||||
  const { resource, field, selectedRowKeys, setSelectedRowKeys, refresh } =
 | 
			
		||||
    useTableContext();
 | 
			
		||||
  const rowKey = field.componentProps.rowKey || 'id';
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    async run() {
 | 
			
		||||
      if (ctx && typeof ctx.index !== 'undefined') {
 | 
			
		||||
        field.remove(ctx.index);
 | 
			
		||||
        refresh();
 | 
			
		||||
        // field.remove(ctx.index);
 | 
			
		||||
        await resource.destroy({
 | 
			
		||||
          [rowKey]: ctx.data[rowKey],
 | 
			
		||||
        });
 | 
			
		||||
        await refresh();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!selectedRowKeys.length) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      while (selectedRowKeys.length) {
 | 
			
		||||
        const key = selectedRowKeys.shift();
 | 
			
		||||
        const index = findIndex(field.value, (item) => item[rowKey] === key);
 | 
			
		||||
        field.remove(index);
 | 
			
		||||
      }
 | 
			
		||||
      await resource.destroy({
 | 
			
		||||
        [`${rowKey}.in`]: selectedRowKeys,
 | 
			
		||||
      });
 | 
			
		||||
      // while (selectedRowKeys.length) {
 | 
			
		||||
      //   const key = selectedRowKeys.shift();
 | 
			
		||||
      //   const index = findIndex(field.value, (item) => item[rowKey] === key);
 | 
			
		||||
      //   field.remove(index);
 | 
			
		||||
      // }
 | 
			
		||||
      refresh();
 | 
			
		||||
      setSelectedRowKeys([]);
 | 
			
		||||
    },
 | 
			
		||||
@ -305,16 +352,18 @@ export function useTableFilterAction() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useTableCreateAction() {
 | 
			
		||||
  const { field, run: exec, params } = useTableContext();
 | 
			
		||||
  const { resource, field, run: exec, params, refresh } = useTableContext();
 | 
			
		||||
  const [, setVisible] = useContext(VisibleContext);
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  return {
 | 
			
		||||
    async run() {
 | 
			
		||||
      setVisible && setVisible(false);
 | 
			
		||||
      field.push({
 | 
			
		||||
        key: uid(),
 | 
			
		||||
        ...clone(form.values),
 | 
			
		||||
      });
 | 
			
		||||
      await resource.save(form.values);
 | 
			
		||||
      // await refresh();
 | 
			
		||||
      // field.push({
 | 
			
		||||
      //   key: uid(),
 | 
			
		||||
      //   ...clone(form.values),
 | 
			
		||||
      // });
 | 
			
		||||
      form.reset();
 | 
			
		||||
      exec({ ...params, page: 1 });
 | 
			
		||||
    },
 | 
			
		||||
@ -322,14 +371,16 @@ export function useTableCreateAction() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useTableUpdateAction() {
 | 
			
		||||
  const { field, refresh, params } = useTableContext();
 | 
			
		||||
  const { resource, field, refresh, params } = useTableContext();
 | 
			
		||||
  const [, setVisible] = useContext(VisibleContext);
 | 
			
		||||
  const ctx = useContext(TableRowContext);
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  return {
 | 
			
		||||
    async run() {
 | 
			
		||||
      field.value[ctx.index] = form.values;
 | 
			
		||||
      setVisible && setVisible(false);
 | 
			
		||||
      await resource.save(form.values, {
 | 
			
		||||
        resourceKey: form.values[field.componentProps.rowKey || 'id'],
 | 
			
		||||
      });
 | 
			
		||||
      refresh();
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
@ -342,6 +393,7 @@ const [TableContextProvider, useTableContext] = constate(() => {
 | 
			
		||||
  const [selectedRowKeys, setSelectedRowKeys] = useState(
 | 
			
		||||
    defaultSelectedRowKeys || [],
 | 
			
		||||
  );
 | 
			
		||||
  const resource = Resource.make(field.componentProps.resourceName);
 | 
			
		||||
  console.log({ defaultSelectedRowKeys });
 | 
			
		||||
  const pagination = usePaginationProps();
 | 
			
		||||
  const response = useRequest<{
 | 
			
		||||
@ -349,6 +401,21 @@ const [TableContextProvider, useTableContext] = constate(() => {
 | 
			
		||||
    total: number;
 | 
			
		||||
  }>((params = {}) => {
 | 
			
		||||
    return new Promise((resolve) => {
 | 
			
		||||
      resource.list({}).then((res) => {
 | 
			
		||||
        field.setValue(res?.data || []);
 | 
			
		||||
        resolve({
 | 
			
		||||
          list: res?.data || [],
 | 
			
		||||
          total: res?.meta?.count || res?.data?.length,
 | 
			
		||||
        });
 | 
			
		||||
        console.log(
 | 
			
		||||
          'field.componentProps.resourceName',
 | 
			
		||||
          field.componentProps.resourceName,
 | 
			
		||||
          res,
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
      const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
 | 
			
		||||
      if (pagination) {
 | 
			
		||||
        const {
 | 
			
		||||
@ -375,6 +442,7 @@ const [TableContextProvider, useTableContext] = constate(() => {
 | 
			
		||||
    ...(response.params[0] || {}),
 | 
			
		||||
  };
 | 
			
		||||
  return {
 | 
			
		||||
    resource,
 | 
			
		||||
    ...response,
 | 
			
		||||
    params,
 | 
			
		||||
    field,
 | 
			
		||||
@ -440,7 +508,10 @@ const TableContainer = observer((props) => {
 | 
			
		||||
          type: 'checkbox',
 | 
			
		||||
          selectedRowKeys,
 | 
			
		||||
          renderCell: (checked, record, _, originNode) => {
 | 
			
		||||
            const index = dataSource.indexOf(record);
 | 
			
		||||
            const index = findIndex(
 | 
			
		||||
              dataSource,
 | 
			
		||||
              (item) => item[rowKey] === record[rowKey],
 | 
			
		||||
            );
 | 
			
		||||
            return (
 | 
			
		||||
              <TableRowContext.Provider
 | 
			
		||||
                value={{
 | 
			
		||||
@ -571,13 +642,79 @@ function useDesignableBar() {
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Table.Column = observer((props) => {
 | 
			
		||||
  const schema = useFieldSchema();
 | 
			
		||||
  const field = useField();
 | 
			
		||||
  const { DesignableBar } = useDesignableBar();
 | 
			
		||||
Table.Cell = observer((props: any) => {
 | 
			
		||||
  const ctx = useContext(TableRowContext);
 | 
			
		||||
  const schema = props.schema;
 | 
			
		||||
  const resourceField = useContext(ResourceFieldContext);
 | 
			
		||||
  return (
 | 
			
		||||
    <RecursionField
 | 
			
		||||
      schema={
 | 
			
		||||
        !resourceField
 | 
			
		||||
          ? schema
 | 
			
		||||
          : new Schema({
 | 
			
		||||
              type: 'void',
 | 
			
		||||
              properties: {
 | 
			
		||||
                [resourceField.name]: {
 | 
			
		||||
                  ...resourceField.uiSchema,
 | 
			
		||||
                  title: undefined,
 | 
			
		||||
                  'x-read-pretty': true,
 | 
			
		||||
                  'x-decorator-props': {
 | 
			
		||||
                    feedbackLayout: 'popover',
 | 
			
		||||
                  },
 | 
			
		||||
                  'x-decorator': 'FormilyFormItem',
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            })
 | 
			
		||||
      }
 | 
			
		||||
      name={ctx.index}
 | 
			
		||||
      onlyRenderProperties
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
  // const { fieldName } = props;
 | 
			
		||||
  // const { schema } = useDesignable();
 | 
			
		||||
  // const path = getSchemaPath(schema);
 | 
			
		||||
  // const { addDisplayField } = useDisplayFieldsContext();
 | 
			
		||||
  // const { resource = {} } = useResourceContext();
 | 
			
		||||
  // useEffect(() => {
 | 
			
		||||
  //   if (fieldName) {
 | 
			
		||||
  //     addDisplayField && addDisplayField(fieldName);
 | 
			
		||||
  //   }
 | 
			
		||||
  // }, [fieldName]);
 | 
			
		||||
  // if (!fieldName) {
 | 
			
		||||
  //   return null;
 | 
			
		||||
  // }
 | 
			
		||||
  // const field = resource?.fields?.find((item) => item.name === fieldName);
 | 
			
		||||
  // if (!field) {
 | 
			
		||||
  //   return null;
 | 
			
		||||
  // }
 | 
			
		||||
  // const name = useContext(FormFieldUIDContext);
 | 
			
		||||
  // const title = schema['title'] || field.uiSchema['title'];
 | 
			
		||||
  // return (
 | 
			
		||||
  //   <RecursionField
 | 
			
		||||
  //     name={name}
 | 
			
		||||
  //     schema={
 | 
			
		||||
  //       new Schema({
 | 
			
		||||
  //         'x-path': path,
 | 
			
		||||
  //         type: 'void',
 | 
			
		||||
  //         properties: {
 | 
			
		||||
  //           [field.name]: {
 | 
			
		||||
  //             ...field.uiSchema,
 | 
			
		||||
  //             title,
 | 
			
		||||
  //             'x-decorator': 'FormilyFormItem',
 | 
			
		||||
  //           },
 | 
			
		||||
  //         },
 | 
			
		||||
  //       })
 | 
			
		||||
  //     }
 | 
			
		||||
  //   />
 | 
			
		||||
  // );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Table.Column = observer((props: any) => {
 | 
			
		||||
  const resourceField = useContext(ResourceFieldContext) || {};
 | 
			
		||||
  const { schema, DesignableBar } = useDesignable();
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={'nb-table-column'}>
 | 
			
		||||
      {field.title}
 | 
			
		||||
      {schema.title || resourceField?.uiSchema?.title}
 | 
			
		||||
      <DesignableBar />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
@ -605,52 +742,28 @@ Table.Column.DesignableBar = () => {
 | 
			
		||||
          overlay={
 | 
			
		||||
            <Menu>
 | 
			
		||||
              <Menu.Item
 | 
			
		||||
                onClick={(e) => {
 | 
			
		||||
                onClick={async (e) => {
 | 
			
		||||
                  const title = uid();
 | 
			
		||||
                  field.title = title;
 | 
			
		||||
                  schema.title = title;
 | 
			
		||||
                  refresh();
 | 
			
		||||
                  await updateSchema({
 | 
			
		||||
                    key: schema['key'],
 | 
			
		||||
                    title: title,
 | 
			
		||||
                  });
 | 
			
		||||
                  setVisible(false);
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                点击修改按钮文案
 | 
			
		||||
                编辑列
 | 
			
		||||
              </Menu.Item>
 | 
			
		||||
              <Menu.Item
 | 
			
		||||
                onClick={() => {
 | 
			
		||||
                  remove();
 | 
			
		||||
                  console.log('Table.Column.DesignableBar', { schema });
 | 
			
		||||
                onClick={async () => {
 | 
			
		||||
                  const s = remove();
 | 
			
		||||
                  await removeSchema(s);
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                删除列
 | 
			
		||||
              </Menu.Item>
 | 
			
		||||
              <Menu.Item
 | 
			
		||||
                onClick={() => {
 | 
			
		||||
                  const name = uid();
 | 
			
		||||
                  insertAfter({
 | 
			
		||||
                    name: `column_${name}`,
 | 
			
		||||
                    type: 'void',
 | 
			
		||||
                    title: `字段 ${name}`,
 | 
			
		||||
                    'x-component': 'Table.Column',
 | 
			
		||||
                    'x-component-props': {
 | 
			
		||||
                      // title: 'z1',
 | 
			
		||||
                    },
 | 
			
		||||
                    'x-designable-bar': 'Table.Column.DesignableBar',
 | 
			
		||||
                    properties: {
 | 
			
		||||
                      [name]: {
 | 
			
		||||
                        type: 'string',
 | 
			
		||||
                        required: true,
 | 
			
		||||
                        // 'x-read-pretty': true,
 | 
			
		||||
                        'x-decorator-props': {
 | 
			
		||||
                          feedbackLayout: 'popover',
 | 
			
		||||
                        },
 | 
			
		||||
                        'x-decorator': 'FormItem',
 | 
			
		||||
                        'x-component': 'Input',
 | 
			
		||||
                      },
 | 
			
		||||
                    },
 | 
			
		||||
                  });
 | 
			
		||||
                }}
 | 
			
		||||
              >
 | 
			
		||||
                插入列
 | 
			
		||||
              </Menu.Item>
 | 
			
		||||
            </Menu>
 | 
			
		||||
          }
 | 
			
		||||
        >
 | 
			
		||||
@ -927,7 +1040,10 @@ Table.Action.DesignableBar = () => {
 | 
			
		||||
              {inActionBar ? (
 | 
			
		||||
                <Menu.Item>放置在右侧</Menu.Item>
 | 
			
		||||
              ) : (
 | 
			
		||||
                <Menu.Item>点击表格行时触发   <Switch size={'small'} defaultChecked/></Menu.Item>
 | 
			
		||||
                <Menu.Item>
 | 
			
		||||
                  点击表格行时触发   
 | 
			
		||||
                  <Switch size={'small'} defaultChecked />
 | 
			
		||||
                </Menu.Item>
 | 
			
		||||
              )}
 | 
			
		||||
 | 
			
		||||
              <Menu.Divider />
 | 
			
		||||
 | 
			
		||||
@ -30,9 +30,10 @@ export const createOrUpdate = async (ctx: actions.Context, next: actions.Next) =
 | 
			
		||||
      await collection.update(values);
 | 
			
		||||
    }
 | 
			
		||||
    await collection.updateAssociations(values);
 | 
			
		||||
    await collection.migrate();
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.log('error.errors', error.errors)
 | 
			
		||||
    throw error;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  ctx.body = collection;
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,16 @@
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import { Model } from '@nocobase/database';
 | 
			
		||||
import { Model, TableOptions } from '@nocobase/database';
 | 
			
		||||
 | 
			
		||||
export interface LoadOptions {
 | 
			
		||||
  reset?: boolean;
 | 
			
		||||
  where?: any;
 | 
			
		||||
  skipExisting?: boolean;
 | 
			
		||||
  [key: string]: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface MigrateOptions {
 | 
			
		||||
  [key: string]: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class Collection extends Model {
 | 
			
		||||
  static async create(value?: any, options?: any): Promise<any> {
 | 
			
		||||
@ -37,4 +48,66 @@ export class Collection extends Model {
 | 
			
		||||
    }
 | 
			
		||||
    return items;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * DOTO:
 | 
			
		||||
   * - database.table 初始化可能存在一些缺陷
 | 
			
		||||
   * - 是否需要考虑关系数据的重载?
 | 
			
		||||
   *
 | 
			
		||||
   * @param opts 
 | 
			
		||||
   */
 | 
			
		||||
  async loadTableOptions(opts: any = {}) {
 | 
			
		||||
    const options = await this.toProps();
 | 
			
		||||
    console.log(JSON.stringify(options, null, 2));
 | 
			
		||||
    const table = this.database.table(options);
 | 
			
		||||
    return table;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 迁移
 | 
			
		||||
   */
 | 
			
		||||
  async migrate(options: MigrateOptions = {}) {
 | 
			
		||||
    const { isNewRecord } = options;
 | 
			
		||||
    const table = await this.loadTableOptions(options);
 | 
			
		||||
    // 如果不是新增数据,force 必须为 false
 | 
			
		||||
    if (!isNewRecord) {
 | 
			
		||||
      return await table.sync({
 | 
			
		||||
        force: false,
 | 
			
		||||
        alter: {
 | 
			
		||||
          drop: false,
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    // TODO: 暂时加了个 collectionSync 解决 collection.create 的数据不清空问题
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const sync = this.sequelize.options.collectionSync;
 | 
			
		||||
    return await table.sync(sync || {
 | 
			
		||||
      force: false,
 | 
			
		||||
      alter: {
 | 
			
		||||
        drop: false,
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * TODO:需要考虑是初次加载还是重载
 | 
			
		||||
   *
 | 
			
		||||
   * @param options 
 | 
			
		||||
   */
 | 
			
		||||
  static async load(options: LoadOptions = {}) {
 | 
			
		||||
    const { skipExisting = false, reset = false, where = {}, transaction } = options;
 | 
			
		||||
    const collections = await this.findAll({
 | 
			
		||||
      transaction,
 | 
			
		||||
      where,
 | 
			
		||||
    });
 | 
			
		||||
    for (const collection of collections) {
 | 
			
		||||
      if (skipExisting && this.database.isDefined(collection.get('name'))) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      await collection.loadTableOptions({
 | 
			
		||||
        transaction,
 | 
			
		||||
        reset,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ export class Field extends Model {
 | 
			
		||||
    const data: any = _.omit(json, ['options', 'created_at', 'updated_at']);
 | 
			
		||||
    const options = json['options'] || {};
 | 
			
		||||
    const fields = await this.getNestedFields();
 | 
			
		||||
    const props = { ...data, ...options };
 | 
			
		||||
    const props = { type: json['dataType'], ...data, ...options };
 | 
			
		||||
    if (fields.length) {
 | 
			
		||||
      props['children'] = fields;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user