updates...
This commit is contained in:
		
							parent
							
								
									03da153052
								
							
						
					
					
						commit
						61796cbc27
					
				@ -27,6 +27,7 @@
 | 
			
		||||
    "beautiful-react-hooks": "^0.35.0",
 | 
			
		||||
    "constate": "^3.3.0",
 | 
			
		||||
    "lodash": "^4.17.21",
 | 
			
		||||
    "monaco-editor": "^0.25.2",
 | 
			
		||||
    "react-dnd": "^14.0.2",
 | 
			
		||||
    "react-dnd-html5-backend": "^14.0.0",
 | 
			
		||||
    "react-dnd-preview": "^6.0.2",
 | 
			
		||||
 | 
			
		||||
@ -1,51 +0,0 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { createFromIconfontCN } from '@ant-design/icons';
 | 
			
		||||
import * as antIcons from '@ant-design/icons';
 | 
			
		||||
 | 
			
		||||
export const IconFont = createFromIconfontCN({
 | 
			
		||||
  scriptUrl: ['//at.alicdn.com/t/font_2261954_u9jzwc44ug.js'],
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const icons = new Map<string, any>();
 | 
			
		||||
 | 
			
		||||
export function registerIcon(type: string, icon: any = IconFont) {
 | 
			
		||||
  icons.set(type.toLowerCase(), icon);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function hasIcon(type: string) {
 | 
			
		||||
  if (!type) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return icons.has(type.toLowerCase());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function registerIcons(components) {
 | 
			
		||||
  Object.keys(components).forEach((type) => {
 | 
			
		||||
    registerIcon(type, components[type]);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Object.keys(antIcons).forEach((name) => {
 | 
			
		||||
  if (name.endsWith('Outlined')) {
 | 
			
		||||
    registerIcon(name, antIcons[name]);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
interface IconProps {
 | 
			
		||||
  type: string;
 | 
			
		||||
  [key: string]: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function Icon(props: IconProps) {
 | 
			
		||||
  const { type = '', ...restProps } = props;
 | 
			
		||||
  if (type && icons.has(type.toLowerCase())) {
 | 
			
		||||
    const IconComponent = icons.get(type.toLowerCase());
 | 
			
		||||
    if (IconComponent === IconFont) {
 | 
			
		||||
      return <IconFont type={type} />;
 | 
			
		||||
    }
 | 
			
		||||
    return <IconComponent {...restProps} />;
 | 
			
		||||
  }
 | 
			
		||||
  return <IconFont type={type} />;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default Icon;
 | 
			
		||||
@ -0,0 +1,50 @@
 | 
			
		||||
import { Button } from 'antd';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { SchemaRenderer, useDesignable, useSchema } from '..';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
 | 
			
		||||
const Hello = () => {
 | 
			
		||||
  return <div>Hello</div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AddNew = () => {
 | 
			
		||||
  const { schema, refresh, insertBefore } = useDesignable();
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Button
 | 
			
		||||
        onClick={() => {
 | 
			
		||||
          insertBefore({
 | 
			
		||||
            type: 'void',
 | 
			
		||||
            'x-component': 'Hello',
 | 
			
		||||
          });
 | 
			
		||||
          refresh();
 | 
			
		||||
          console.log({ schema });
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        Add New
 | 
			
		||||
      </Button>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <SchemaRenderer
 | 
			
		||||
      debug={true}
 | 
			
		||||
      components={{ Hello, AddNew }}
 | 
			
		||||
      schema={{
 | 
			
		||||
        type: 'object',
 | 
			
		||||
        properties: {
 | 
			
		||||
          hello: {
 | 
			
		||||
            type: 'void',
 | 
			
		||||
            'x-component': 'Hello',
 | 
			
		||||
          },
 | 
			
		||||
          addnew: {
 | 
			
		||||
            type: 'void',
 | 
			
		||||
            'x-component': 'AddNew',
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      }}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1
									
								
								packages/client/src/components/schema-renderer/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/client/src/components/schema-renderer/index.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
<code src="./demos/demo1.tsx"/>
 | 
			
		||||
@ -1,46 +1,51 @@
 | 
			
		||||
import React, {
 | 
			
		||||
  createContext,
 | 
			
		||||
  useCallback,
 | 
			
		||||
  useContext,
 | 
			
		||||
  useMemo,
 | 
			
		||||
  useState,
 | 
			
		||||
} from 'react';
 | 
			
		||||
import { createForm } from '@formily/core';
 | 
			
		||||
import {
 | 
			
		||||
  Field,
 | 
			
		||||
  ISchema,
 | 
			
		||||
  observer,
 | 
			
		||||
  Schema,
 | 
			
		||||
  createSchemaField,
 | 
			
		||||
  FormProvider,
 | 
			
		||||
  useField,
 | 
			
		||||
  ISchema,
 | 
			
		||||
  Schema,
 | 
			
		||||
  SchemaOptionsContext,
 | 
			
		||||
  useFieldSchema,
 | 
			
		||||
  useForm,
 | 
			
		||||
  SchemaOptionsContext,
 | 
			
		||||
} from '@formily/react';
 | 
			
		||||
import { observable } from '@formily/reactive';
 | 
			
		||||
import { uid, clone } from '@formily/shared';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
import constate from 'constate';
 | 
			
		||||
import { get } from 'lodash';
 | 
			
		||||
import React, { createContext } from 'react';
 | 
			
		||||
import { useState } from 'react';
 | 
			
		||||
import { useContext } from 'react';
 | 
			
		||||
import { useMemo } from 'react';
 | 
			
		||||
 | 
			
		||||
import { CodeOutlined } from '@ant-design/icons';
 | 
			
		||||
import Editor from '@monaco-editor/react';
 | 
			
		||||
 | 
			
		||||
import { ArrayCollapse, ArrayTable, FormLayout } from '@formily/antd';
 | 
			
		||||
 | 
			
		||||
import { Space, Card, Modal, Spin } from 'antd';
 | 
			
		||||
import { Action, useLogin, useRegister, useSubmit, useDesignableValues } from '../action';
 | 
			
		||||
import { AddNew } from '../add-new';
 | 
			
		||||
import { Cascader } from '../cascader';
 | 
			
		||||
import { Checkbox } from '../checkbox';
 | 
			
		||||
import { ColorSelect } from '../color-select';
 | 
			
		||||
import { DatabaseField } from '../database-field';
 | 
			
		||||
import { DatePicker } from '../date-picker';
 | 
			
		||||
import { Filter } from '../filter';
 | 
			
		||||
import { Form } from '../form';
 | 
			
		||||
import { Grid } from '../grid';
 | 
			
		||||
import { IconPicker } from '../icon-picker';
 | 
			
		||||
import { Input } from '../input';
 | 
			
		||||
import { InputNumber } from '../input-number';
 | 
			
		||||
import { Markdown } from '../markdown';
 | 
			
		||||
import { Menu } from '../menu';
 | 
			
		||||
import { Password } from '../password';
 | 
			
		||||
import { Radio } from '../radio';
 | 
			
		||||
import { Select } from '../select';
 | 
			
		||||
import {
 | 
			
		||||
  Action,
 | 
			
		||||
  useLogin,
 | 
			
		||||
  useRegister,
 | 
			
		||||
  useSubmit,
 | 
			
		||||
  useDesignableValues,
 | 
			
		||||
} from '../../schemas/action';
 | 
			
		||||
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 { DatePicker } from '../../schemas/date-picker';
 | 
			
		||||
import { Filter } from '../../schemas/filter';
 | 
			
		||||
import { Form } from '../../schemas/form';
 | 
			
		||||
import { Grid } from '../../schemas/grid';
 | 
			
		||||
import { IconPicker } from '../../schemas/icon-picker';
 | 
			
		||||
import { Input } from '../../schemas/input';
 | 
			
		||||
import { InputNumber } from '../../schemas/input-number';
 | 
			
		||||
import { Markdown } from '../../schemas/markdown';
 | 
			
		||||
import { Menu } from '../../schemas/menu';
 | 
			
		||||
import { Password } from '../../schemas/password';
 | 
			
		||||
import { Radio } from '../../schemas/radio';
 | 
			
		||||
import { Select } from '../../schemas/select';
 | 
			
		||||
import {
 | 
			
		||||
  Table,
 | 
			
		||||
  useTableRow,
 | 
			
		||||
@ -48,23 +53,25 @@ import {
 | 
			
		||||
  useTableUpdateAction,
 | 
			
		||||
  useTableDestroyAction,
 | 
			
		||||
  useTableFilterAction,
 | 
			
		||||
} from '../table';
 | 
			
		||||
import { Tabs } from '../tabs';
 | 
			
		||||
import { TimePicker } from '../time-picker';
 | 
			
		||||
import { Upload } from '../upload';
 | 
			
		||||
import { FormItem } from '../form-item';
 | 
			
		||||
import { BlockItem } from '../block-item';
 | 
			
		||||
import { DragAndDrop } from '../drag-and-drop';
 | 
			
		||||
 | 
			
		||||
import { CodeOutlined } from '@ant-design/icons';
 | 
			
		||||
import Editor from '@monaco-editor/react';
 | 
			
		||||
import { get } from 'lodash';
 | 
			
		||||
} from '../../schemas/table';
 | 
			
		||||
import { Tabs } from '../../schemas/tabs';
 | 
			
		||||
import { TimePicker } from '../../schemas/time-picker';
 | 
			
		||||
import { Upload } from '../../schemas/upload';
 | 
			
		||||
import { FormItem } from '../../schemas/form-item';
 | 
			
		||||
import { BlockItem } from '../../schemas/block-item';
 | 
			
		||||
import { DragAndDrop } from '../../schemas/drag-and-drop';
 | 
			
		||||
 | 
			
		||||
export const BlockContext = createContext({ dragRef: null });
 | 
			
		||||
 | 
			
		||||
const Div = (props) => <div {...props} />;
 | 
			
		||||
 | 
			
		||||
export const scope = {
 | 
			
		||||
interface DesignableContextProps {
 | 
			
		||||
  schema?: Schema;
 | 
			
		||||
  refresh?: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const SchemaField = createSchemaField({
 | 
			
		||||
  scope: {
 | 
			
		||||
    useLogin,
 | 
			
		||||
    useRegister,
 | 
			
		||||
    useSubmit,
 | 
			
		||||
@ -74,9 +81,8 @@ export const scope = {
 | 
			
		||||
    useTableRow,
 | 
			
		||||
    useTableUpdateAction,
 | 
			
		||||
    useDesignableValues,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const components = {
 | 
			
		||||
  },
 | 
			
		||||
  components: {
 | 
			
		||||
    Card,
 | 
			
		||||
    Div,
 | 
			
		||||
    Space,
 | 
			
		||||
@ -112,30 +118,16 @@ export const components = {
 | 
			
		||||
    Tabs,
 | 
			
		||||
    TimePicker,
 | 
			
		||||
    Upload,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function registerScope(scopes) {
 | 
			
		||||
  Object.keys(scopes).forEach((key) => {
 | 
			
		||||
    scope[key] = scopes[key];
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function registerComponents(values) {
 | 
			
		||||
  Object.keys(values).forEach((key) => {
 | 
			
		||||
    components[key] = values[key];
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface DesignableContextProps {
 | 
			
		||||
  schema: Schema;
 | 
			
		||||
  refresh: () => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const DesignableContext = createContext<DesignableContextProps>({
 | 
			
		||||
  schema: null,
 | 
			
		||||
  refresh: null,
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const DesignableContext = createContext<DesignableContextProps>(null);
 | 
			
		||||
 | 
			
		||||
export function useSchema(path?: any) {
 | 
			
		||||
  const { schema, refresh } = useContext(DesignableContext);
 | 
			
		||||
  return { schema, refresh };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function pathToArray(path): string[] {
 | 
			
		||||
  if (Array.isArray(path)) {
 | 
			
		||||
    return [...path];
 | 
			
		||||
@ -188,13 +180,16 @@ export function addPropertyAfter(target: Schema, data: ISchema) {
 | 
			
		||||
export function useDesignable(path?: any) {
 | 
			
		||||
  const { schema, refresh } = useContext(DesignableContext);
 | 
			
		||||
  const schemaPath = path || useSchemaPath();
 | 
			
		||||
  const currentSchema = findPropertyByPath(schema, schemaPath) || ({} as Schema);
 | 
			
		||||
  console.log('useDesignable', { schema, schemaPath, currentSchema });
 | 
			
		||||
  const fieldSchema = useFieldSchema();
 | 
			
		||||
  const currentSchema =
 | 
			
		||||
    findPropertyByPath(schema, schemaPath) || ({} as Schema);
 | 
			
		||||
  // console.log('useDesignable', { schema, schemaPath, currentSchema });
 | 
			
		||||
  const options = useContext(SchemaOptionsContext);
 | 
			
		||||
  const DesignableBar = get(options.components, currentSchema['x-designable-bar']) || (() => null);
 | 
			
		||||
  const DesignableBar =
 | 
			
		||||
    get(options.components, currentSchema['x-designable-bar']) || (() => null);
 | 
			
		||||
  return {
 | 
			
		||||
    DesignableBar,
 | 
			
		||||
    schema: currentSchema,
 | 
			
		||||
    schema: currentSchema || fieldSchema,
 | 
			
		||||
    refresh,
 | 
			
		||||
    prepend: (property: ISchema, targetPath?: any): Schema => {
 | 
			
		||||
      let target = currentSchema;
 | 
			
		||||
@ -218,7 +213,7 @@ export function useDesignable(path?: any) {
 | 
			
		||||
        current.parent.removeProperty(current.name);
 | 
			
		||||
        properties[current.name] = current.toJSON();
 | 
			
		||||
      });
 | 
			
		||||
      console.log({ properties }, target.properties);
 | 
			
		||||
      // console.log({ properties }, target.properties);
 | 
			
		||||
      target.setProperties(properties);
 | 
			
		||||
      refresh();
 | 
			
		||||
      return target.properties[property.name];
 | 
			
		||||
@ -331,7 +326,7 @@ export function getSchemaPath(schema: Schema) {
 | 
			
		||||
    path.unshift(parent.name);
 | 
			
		||||
    parent = parent.parent;
 | 
			
		||||
  }
 | 
			
		||||
  console.log('getSchemaPath', path, schema);
 | 
			
		||||
  // console.log('getSchemaPath', path, schema);
 | 
			
		||||
  return [...path];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -341,38 +336,6 @@ export function useSchemaPath() {
 | 
			
		||||
  return [...path];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
console.log({ scope, components });
 | 
			
		||||
 | 
			
		||||
export const createDesignableSchemaField = (options) => {
 | 
			
		||||
  const SchemaField = createSchemaField(options);
 | 
			
		||||
 | 
			
		||||
  const DesignableSchemaField = (props) => {
 | 
			
		||||
 | 
			
		||||
    const schema = useMemo(() => new Schema(props.schema), [props.schema]);
 | 
			
		||||
    const [, refresh] = useState(0);
 | 
			
		||||
    if (props.designable === false) {
 | 
			
		||||
      return <SchemaField schema={schema} />;
 | 
			
		||||
    }
 | 
			
		||||
    return (
 | 
			
		||||
      <DesignableContext.Provider
 | 
			
		||||
        value={{
 | 
			
		||||
          schema,
 | 
			
		||||
          refresh: () => {
 | 
			
		||||
            refresh(Math.random());
 | 
			
		||||
            props.onRefresh && props.onRefresh(schema);
 | 
			
		||||
          },
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <SchemaField scope={props.scope} components={props.components} schema={schema} />
 | 
			
		||||
        {props.debug && <CodePreview schema={schema} />}
 | 
			
		||||
        {props.debug && <FormValues />}
 | 
			
		||||
      </DesignableContext.Provider>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return DesignableSchemaField;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const FormValues = () => {
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  const [visible, setVisible] = useState(false);
 | 
			
		||||
@ -400,7 +363,10 @@ const CodePreview = ({ schema }) => {
 | 
			
		||||
  const [visible, setVisible] = useState(false);
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <CodeOutlined style={{position: 'relative', zIndex: 100}} onClick={() => setVisible(true)} />
 | 
			
		||||
      <CodeOutlined
 | 
			
		||||
        style={{ position: 'relative', zIndex: 100 }}
 | 
			
		||||
        onClick={() => setVisible(true)}
 | 
			
		||||
      />
 | 
			
		||||
      <Modal
 | 
			
		||||
        width={'50%'}
 | 
			
		||||
        onOk={() => setVisible(false)}
 | 
			
		||||
@ -418,31 +384,25 @@ const CodePreview = ({ schema }) => {
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const SchemaField = createSchemaField({
 | 
			
		||||
  scope,
 | 
			
		||||
  components,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const DesignableSchemaField = createDesignableSchemaField({
 | 
			
		||||
  scope,
 | 
			
		||||
  components,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export interface SchemaRendererProps {
 | 
			
		||||
  schema: ISchema;
 | 
			
		||||
  schema: Schema | ISchema;
 | 
			
		||||
  form?: any;
 | 
			
		||||
  designable?: boolean;
 | 
			
		||||
  onRefresh?: any;
 | 
			
		||||
  onlyRenderProperties?: boolean;
 | 
			
		||||
  scope?: any;
 | 
			
		||||
  render?: any;
 | 
			
		||||
  components?: any;
 | 
			
		||||
  scope?: any;
 | 
			
		||||
  debug?: boolean;
 | 
			
		||||
  onlyRenderProperties?: boolean;
 | 
			
		||||
  onRefresh?: any;
 | 
			
		||||
  [key: string]: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const SchemaRenderer = (props: SchemaRendererProps) => {
 | 
			
		||||
  const form = useMemo(() => props.form || createForm({}), []);
 | 
			
		||||
 | 
			
		||||
  const [, refresh] = useState(uid());
 | 
			
		||||
  const form = useMemo(() => props.form || createForm(), []);
 | 
			
		||||
  const schema = useMemo(() => {
 | 
			
		||||
    if (Schema.isSchemaInstance(props.schema)) {
 | 
			
		||||
      return schema;
 | 
			
		||||
    }
 | 
			
		||||
    let s = props.schema;
 | 
			
		||||
    if (props.onlyRenderProperties) {
 | 
			
		||||
      s = {
 | 
			
		||||
@ -457,21 +417,32 @@ export const SchemaRenderer = (props: SchemaRendererProps) => {
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    return s;
 | 
			
		||||
    return new Schema(s);
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  console.log('SchemaRenderer', schema, props.schema);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
  const defaultRender = ({ schema }) => (
 | 
			
		||||
    <FormProvider form={form}>
 | 
			
		||||
      <DesignableSchemaField
 | 
			
		||||
        debug={props.debug}
 | 
			
		||||
        scope={props.scope}
 | 
			
		||||
      <SchemaField
 | 
			
		||||
        components={props.components}
 | 
			
		||||
        onRefresh={props.onRefresh}
 | 
			
		||||
        designable={props.designable}
 | 
			
		||||
        scope={props.scope}
 | 
			
		||||
        schema={schema}
 | 
			
		||||
      />
 | 
			
		||||
      {props.debug && <CodePreview schema={schema} />}
 | 
			
		||||
      {props.debug && <FormValues />}
 | 
			
		||||
    </FormProvider>
 | 
			
		||||
  );
 | 
			
		||||
  return (
 | 
			
		||||
    <DesignableContext.Provider
 | 
			
		||||
      value={{
 | 
			
		||||
        schema,
 | 
			
		||||
        refresh: () => {
 | 
			
		||||
          props.onRefresh && props.onRefresh(schema);
 | 
			
		||||
          refresh(uid());
 | 
			
		||||
        },
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <DesignableContext.Consumer>
 | 
			
		||||
        {props.render || defaultRender}
 | 
			
		||||
      </DesignableContext.Consumer>
 | 
			
		||||
    </DesignableContext.Provider>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										30
									
								
								packages/client/src/schemas/action/demos/demo1.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								packages/client/src/schemas/action/demos/demo1.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
/**
 | 
			
		||||
 * title: 按钮操作
 | 
			
		||||
 * desc: 可以通过配置 `useAction` 来处理操作逻辑
 | 
			
		||||
 */
 | 
			
		||||
import React from 'react';
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
import { SchemaRenderer } from '@nocobase/client';
 | 
			
		||||
 | 
			
		||||
function useAction() {
 | 
			
		||||
  return {
 | 
			
		||||
    run() {
 | 
			
		||||
      alert('这是自定义的操作逻辑');
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '按钮',
 | 
			
		||||
  'x-component': 'Action',
 | 
			
		||||
  'x-designable-bar': 'Action.DesignableBar',
 | 
			
		||||
  'x-component-props': {
 | 
			
		||||
    useAction: '{{ useAction }}',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return <SchemaRenderer debug={true} scope={{ useAction }} schema={schema} />;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										16
									
								
								packages/client/src/schemas/action/demos/demo2.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/client/src/schemas/action/demos/demo2.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { SchemaRenderer } from '@nocobase/client';
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '按钮',
 | 
			
		||||
  'x-component': 'Action.Link',
 | 
			
		||||
  'x-component-props': {
 | 
			
		||||
    to: 'abc',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return <SchemaRenderer schema={schema} />;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										16
									
								
								packages/client/src/schemas/action/demos/demo3.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/client/src/schemas/action/demos/demo3.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { SchemaRenderer } from '@nocobase/client';
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '按钮',
 | 
			
		||||
  'x-component': 'Action.URL',
 | 
			
		||||
  'x-component-props': {
 | 
			
		||||
    url: 'https://www.nocobase.com/',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return <SchemaRenderer schema={schema} />;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										92
									
								
								packages/client/src/schemas/action/demos/demo4.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								packages/client/src/schemas/action/demos/demo4.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,92 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { SchemaRenderer } from '@nocobase/client';
 | 
			
		||||
 | 
			
		||||
function useAction() {
 | 
			
		||||
  return {
 | 
			
		||||
    run() {
 | 
			
		||||
      alert('这是自定义的操作逻辑');
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '下拉菜单',
 | 
			
		||||
  'x-component': 'Action.Dropdown',
 | 
			
		||||
  properties: {
 | 
			
		||||
    action1: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      name: 'action1',
 | 
			
		||||
      title: '按钮',
 | 
			
		||||
      'x-component': 'Action',
 | 
			
		||||
      'x-component-props': {
 | 
			
		||||
        useAction,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    action2: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      title: 'Drawer 按钮',
 | 
			
		||||
      'x-component': 'Action',
 | 
			
		||||
      properties: {
 | 
			
		||||
        drawer1: {
 | 
			
		||||
          type: 'void',
 | 
			
		||||
          title: '抽屉标题',
 | 
			
		||||
          'x-component': 'Action.Drawer',
 | 
			
		||||
          'x-component-props': {},
 | 
			
		||||
          properties: {
 | 
			
		||||
            input: {
 | 
			
		||||
              type: 'string',
 | 
			
		||||
              'x-decorator': 'FormItem',
 | 
			
		||||
              'x-component': 'Input',
 | 
			
		||||
            },
 | 
			
		||||
            action2: {
 | 
			
		||||
              type: 'void',
 | 
			
		||||
              title: '打开二级抽屉',
 | 
			
		||||
              // 'x-decorator': 'FormItem',
 | 
			
		||||
              'x-component': 'Action',
 | 
			
		||||
              properties: {
 | 
			
		||||
                drawer1: {
 | 
			
		||||
                  type: 'void',
 | 
			
		||||
                  title: '二级抽屉标题',
 | 
			
		||||
                  'x-component': 'Action.Drawer',
 | 
			
		||||
                  'x-component-props': {},
 | 
			
		||||
                  properties: {
 | 
			
		||||
                    input: {
 | 
			
		||||
                      type: 'string',
 | 
			
		||||
                      'x-component': 'Input',
 | 
			
		||||
                    },
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    action3: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      name: 'action1',
 | 
			
		||||
      title: 'Popover 按钮',
 | 
			
		||||
      'x-component': 'Action',
 | 
			
		||||
      properties: {
 | 
			
		||||
        popover1: {
 | 
			
		||||
          type: 'void',
 | 
			
		||||
          title: '弹窗标题',
 | 
			
		||||
          'x-component': 'Action.Popover',
 | 
			
		||||
          'x-component-props': {},
 | 
			
		||||
          properties: {
 | 
			
		||||
            input: {
 | 
			
		||||
              type: 'string',
 | 
			
		||||
              'x-component': 'Input',
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return <SchemaRenderer schema={schema} />;
 | 
			
		||||
};
 | 
			
		||||
@ -77,180 +77,19 @@ group:
 | 
			
		||||
 | 
			
		||||
### Action - 常规操作
 | 
			
		||||
 | 
			
		||||
```tsx
 | 
			
		||||
/**
 | 
			
		||||
 * title: 按钮操作
 | 
			
		||||
 * desc: 可以通过配置 `useAction` 来处理操作逻辑
 | 
			
		||||
 */
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { SchemaRenderer, registerScope } from '../';
 | 
			
		||||
 | 
			
		||||
function useAction() {
 | 
			
		||||
  return {
 | 
			
		||||
    run() {
 | 
			
		||||
      alert('这是自定义的操作逻辑');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '按钮',
 | 
			
		||||
  'x-component': 'Action',
 | 
			
		||||
  'x-designable-bar': 'Action.DesignableBar',
 | 
			
		||||
  'x-component-props': {
 | 
			
		||||
    useAction: '{{ useAction }}',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return <SchemaRenderer debug={true} scope={{ useAction }} schema={schema} />
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
<code src="./demos/demo1.tsx">
 | 
			
		||||
 | 
			
		||||
### Action.Link - 内链跳转
 | 
			
		||||
 | 
			
		||||
```tsx
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { SchemaRenderer } from '../';
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '按钮',
 | 
			
		||||
  'x-component': 'Action.Link',
 | 
			
		||||
  'x-component-props': {
 | 
			
		||||
    to: 'abc',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return <SchemaRenderer schema={schema} />
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
<code src="./demos/demo2.tsx">
 | 
			
		||||
 | 
			
		||||
### Action.URL - 外链跳转
 | 
			
		||||
 | 
			
		||||
```tsx
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { SchemaRenderer } from '../';
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '按钮',
 | 
			
		||||
  'x-component': 'Action.URL',
 | 
			
		||||
  'x-component-props': {
 | 
			
		||||
    url: 'https://www.nocobase.com/',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return <SchemaRenderer schema={schema} />
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
<code src="./demos/demo3.tsx">
 | 
			
		||||
 | 
			
		||||
### Action.Dropdown - 下拉操作
 | 
			
		||||
 | 
			
		||||
```tsx
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { SchemaRenderer } from '../';
 | 
			
		||||
 | 
			
		||||
function useAction() {
 | 
			
		||||
  return {
 | 
			
		||||
    run() {
 | 
			
		||||
      alert('这是自定义的操作逻辑');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const popoverSchema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '按钮',
 | 
			
		||||
  'x-component': 'Action',
 | 
			
		||||
  properties: {
 | 
			
		||||
    popover1: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      title: '弹窗标题',
 | 
			
		||||
      'x-component': 'Action.Popover',
 | 
			
		||||
      'x-component-props': {},
 | 
			
		||||
      properties: {
 | 
			
		||||
        input: {
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          'x-component': 'Input',
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const drawerSchema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  title: '按钮',
 | 
			
		||||
  'x-component': 'Action',
 | 
			
		||||
  properties: {
 | 
			
		||||
    drawer1: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      title: '抽屉标题',
 | 
			
		||||
      'x-component': 'Action.Drawer',
 | 
			
		||||
      'x-component-props': {},
 | 
			
		||||
      properties: {
 | 
			
		||||
        input: {
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          'x-decorator': 'FormItem',
 | 
			
		||||
          'x-component': 'Input',
 | 
			
		||||
        },
 | 
			
		||||
        action2: {
 | 
			
		||||
          type: 'void',
 | 
			
		||||
          title: '打开二级抽屉',
 | 
			
		||||
          // 'x-decorator': 'FormItem',
 | 
			
		||||
          'x-component': 'Action',
 | 
			
		||||
          properties: {
 | 
			
		||||
            drawer1: {
 | 
			
		||||
              type: 'void',
 | 
			
		||||
              title: '二级抽屉标题',
 | 
			
		||||
              'x-component': 'Action.Drawer',
 | 
			
		||||
              'x-component-props': {},
 | 
			
		||||
              properties: {
 | 
			
		||||
                input: {
 | 
			
		||||
                  type: 'string',
 | 
			
		||||
                  'x-component': 'Input',
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '下拉菜单',
 | 
			
		||||
  'x-component': 'Action.Dropdown',
 | 
			
		||||
  properties: {
 | 
			
		||||
    action1: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      name: 'action1',
 | 
			
		||||
      title: '按钮',
 | 
			
		||||
      'x-component': 'Action',
 | 
			
		||||
      'x-component-props': {
 | 
			
		||||
        useAction,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    action2: drawerSchema,
 | 
			
		||||
    action3: popoverSchema,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return <SchemaRenderer schema={schema} />
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
<code src="./demos/demo4.tsx">
 | 
			
		||||
 | 
			
		||||
### Action.Popover - 打开气泡
 | 
			
		||||
 | 
			
		||||
@ -288,9 +127,10 @@ export default () => {
 | 
			
		||||
 | 
			
		||||
```tsx
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { Space } from 'antd';
 | 
			
		||||
import { SchemaRenderer } from '../';
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
const schema1 = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: '按钮',
 | 
			
		||||
@ -332,8 +172,40 @@ const schema = {
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const schema2 = {
 | 
			
		||||
  type: 'void',
 | 
			
		||||
  name: 'action1',
 | 
			
		||||
  title: 'x-decorator=Form',
 | 
			
		||||
  'x-component': 'Action',
 | 
			
		||||
  properties: {
 | 
			
		||||
    drawer1: {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      title: '弹窗标题',
 | 
			
		||||
      'x-component': 'Action.Drawer',
 | 
			
		||||
      'x-component-props': {
 | 
			
		||||
        // footer: null,
 | 
			
		||||
      },
 | 
			
		||||
      'x-decorator': 'Form',
 | 
			
		||||
      properties: {
 | 
			
		||||
        input: {
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          title: '输入框',
 | 
			
		||||
          'x-decorator': 'FormItem',
 | 
			
		||||
          'x-component': 'Input',
 | 
			
		||||
          required: true,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return <SchemaRenderer schema={schema} />
 | 
			
		||||
  return (
 | 
			
		||||
    <Space>
 | 
			
		||||
      <SchemaRenderer schema={schema1} />
 | 
			
		||||
      <SchemaRenderer schema={schema2} />
 | 
			
		||||
    </Space>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@ -367,7 +239,9 @@ const schema = {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      title: '弹窗标题',
 | 
			
		||||
      'x-component': 'Action.Modal',
 | 
			
		||||
      'x-component-props': {},
 | 
			
		||||
      'x-component-props': {
 | 
			
		||||
        // footer: null,
 | 
			
		||||
      },
 | 
			
		||||
      properties: {
 | 
			
		||||
        input: {
 | 
			
		||||
          type: 'string',
 | 
			
		||||
@ -418,7 +292,9 @@ const schema2 = {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      title: '弹窗标题',
 | 
			
		||||
      'x-component': 'Action.Modal',
 | 
			
		||||
      'x-component-props': {},
 | 
			
		||||
      'x-component-props': {
 | 
			
		||||
        // footer: null,
 | 
			
		||||
      },
 | 
			
		||||
      'x-decorator': 'Form',
 | 
			
		||||
      properties: {
 | 
			
		||||
        input: {
 | 
			
		||||
 | 
			
		||||
@ -86,24 +86,18 @@ export type ActionType = React.FC<ActionProps> & {
 | 
			
		||||
  DesignableBar?: React.FC<any>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function Blank() {
 | 
			
		||||
  return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function useDesignableBar() {
 | 
			
		||||
  const schema = useFieldSchema();
 | 
			
		||||
  const options = useContext(SchemaOptionsContext);
 | 
			
		||||
  const DesignableBar = get(options.components, schema['x-designable-bar']);
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    DesignableBar: DesignableBar || Blank,
 | 
			
		||||
    DesignableBar: DesignableBar || (() => null),
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const [VisibleProvider, useVisibleContext] = constate((props: any = {}) => {
 | 
			
		||||
  const { initialVisible = false, containerRef = null } = props;
 | 
			
		||||
  const [visible, setVisible] = useState(initialVisible);
 | 
			
		||||
 | 
			
		||||
  return { containerRef, visible, setVisible };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -123,6 +117,7 @@ const BaseAction = observer((props: any) => {
 | 
			
		||||
  const { setVisible } = useVisibleContext();
 | 
			
		||||
  const fieldSchema = useFieldSchema();
 | 
			
		||||
  const { schema } = useDesignable();
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    field.componentProps.setVisible = setVisible;
 | 
			
		||||
  }, []);
 | 
			
		||||
@ -130,7 +125,7 @@ const BaseAction = observer((props: any) => {
 | 
			
		||||
  const renderButton = () => (
 | 
			
		||||
    <ButtonComponent
 | 
			
		||||
      {...others}
 | 
			
		||||
      icon={<Icon type={icon} />}
 | 
			
		||||
      icon={icon ? <Icon type={icon} /> : null}
 | 
			
		||||
      className={classNames(className, `name-${schema.name}`)}
 | 
			
		||||
      onClick={async (e) => {
 | 
			
		||||
        e.stopPropagation && e.stopPropagation();
 | 
			
		||||
@ -237,13 +232,29 @@ Action.Drawer = observer((props) => {
 | 
			
		||||
  const { visible, setVisible } = useVisibleContext();
 | 
			
		||||
  const { run } = useAction();
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  console.log(`schema['x-decorator']`, schema['x-decorator'])
 | 
			
		||||
  console.log(`schema['x-decorator']`, schema['x-decorator']);
 | 
			
		||||
  if (schema['x-decorator'] === 'Form.Decorator') {
 | 
			
		||||
    Object.assign(others, {
 | 
			
		||||
      footer: (
 | 
			
		||||
        <Space>
 | 
			
		||||
          <Button type={'primary'}>Ok</Button>
 | 
			
		||||
          <Button>Cancel</Button>
 | 
			
		||||
          <Button
 | 
			
		||||
            onClick={async () => {
 | 
			
		||||
              await form.submit();
 | 
			
		||||
              await run();
 | 
			
		||||
              setVisible(false);
 | 
			
		||||
            }}
 | 
			
		||||
            type={'primary'}
 | 
			
		||||
          >
 | 
			
		||||
            OK
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button
 | 
			
		||||
            onClick={async () => {
 | 
			
		||||
              form.clearErrors();
 | 
			
		||||
              setVisible(false);
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            Cancel
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Space>
 | 
			
		||||
      ),
 | 
			
		||||
    });
 | 
			
		||||
@ -258,8 +269,10 @@ Action.Drawer = observer((props) => {
 | 
			
		||||
      width={'50%'}
 | 
			
		||||
      {...others}
 | 
			
		||||
      visible={visible}
 | 
			
		||||
      destroyOnClose
 | 
			
		||||
      onClose={(e) => {
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
        form.clearErrors();
 | 
			
		||||
        setVisible(false);
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
@ -273,8 +286,6 @@ const [DesignableContextProvider, useDesignableContext] = constate(() => {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
import { DesignableBar } from './designableBar';
 | 
			
		||||
import { cloneDeep } from 'lodash';
 | 
			
		||||
import { clone } from '@formily/shared';
 | 
			
		||||
 | 
			
		||||
export function useDesignableValues() {
 | 
			
		||||
  const { schema } = useDesignableContext();
 | 
			
		||||
@ -313,7 +324,11 @@ Action.Modal = observer((props) => {
 | 
			
		||||
  const { visible, setVisible } = useVisibleContext();
 | 
			
		||||
  const { run } = useAction();
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  if (schema['x-decorator'] !== 'Form.Decorator') {
 | 
			
		||||
  // console.log('Action.Modal', schema['x-component-props'], Object.keys(others).includes('footer'));
 | 
			
		||||
  if (
 | 
			
		||||
    !Object.keys(others).includes('footer') &&
 | 
			
		||||
    schema['x-decorator'] !== 'Form.Decorator'
 | 
			
		||||
  ) {
 | 
			
		||||
    Object.assign(others, { footer: null });
 | 
			
		||||
  }
 | 
			
		||||
  return (
 | 
			
		||||
@ -328,13 +343,13 @@ Action.Modal = observer((props) => {
 | 
			
		||||
      onCancel={async (e) => {
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
        setVisible(false);
 | 
			
		||||
        // await form.reset();
 | 
			
		||||
        setDropdownVisible && setDropdownVisible(false);
 | 
			
		||||
      }}
 | 
			
		||||
      onOk={async (e) => {
 | 
			
		||||
        console.log('onOk', form.values);
 | 
			
		||||
        // await form.submit();
 | 
			
		||||
        await run();
 | 
			
		||||
        // e.stopPropagation();
 | 
			
		||||
        setVisible(false);
 | 
			
		||||
        setDropdownVisible && setDropdownVisible(false);
 | 
			
		||||
      }}
 | 
			
		||||
@ -348,12 +363,13 @@ Action.Modal = observer((props) => {
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const [DropdownVisibleProvider, useDropdownVisibleContext] = constate((props: any = {}) => {
 | 
			
		||||
const [DropdownVisibleProvider, useDropdownVisibleContext] = constate(
 | 
			
		||||
  (props: any = {}) => {
 | 
			
		||||
    const { initialVisible = false } = props;
 | 
			
		||||
    const [visible, setVisible] = useState(initialVisible);
 | 
			
		||||
 | 
			
		||||
    return { visible, setVisible };
 | 
			
		||||
});
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const ActionDropdown = observer((props: any) => {
 | 
			
		||||
  const { icon, ...others } = props;
 | 
			
		||||
@ -371,7 +387,7 @@ const ActionDropdown = observer((props: any) => {
 | 
			
		||||
      content={props.children}
 | 
			
		||||
      placement={'bottomLeft'}
 | 
			
		||||
    >
 | 
			
		||||
      <Button icon={<Icon type={icon} />} {...others}>
 | 
			
		||||
      <Button icon={icon ? <Icon type={icon} /> : null} {...others}>
 | 
			
		||||
        {schema.title}
 | 
			
		||||
      </Button>
 | 
			
		||||
    </Popover>
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@ import {
 | 
			
		||||
  DownOutlined,
 | 
			
		||||
  DatabaseOutlined,
 | 
			
		||||
} from '@ant-design/icons';
 | 
			
		||||
import { useDesignable, useSchemaPath } from '../DesignableSchemaField';
 | 
			
		||||
import { useDesignable, useSchemaPath } from '../';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
 | 
			
		||||
const generateGridBlock = (schema: ISchema) => {
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ import './style.less';
 | 
			
		||||
import get from 'lodash/get';
 | 
			
		||||
import { GridBlockContext } from '../grid';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
import { useDesignable, useSchemaPath } from '../DesignableSchemaField';
 | 
			
		||||
import { useDesignable, useSchemaPath } from '../';
 | 
			
		||||
import { AddNew } from '../add-new';
 | 
			
		||||
import { Card } from 'antd';
 | 
			
		||||
import {
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ import { ArrayCollapse } from '@formily/antd';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
import '@formily/antd/lib/form-tab/style';
 | 
			
		||||
import { Collapse, Button, Dropdown, Menu, Tag } from 'antd';
 | 
			
		||||
import { interfaces } from './interfaces';
 | 
			
		||||
import { options, interfaces } from './interfaces';
 | 
			
		||||
import { DeleteOutlined } from '@ant-design/icons';
 | 
			
		||||
 | 
			
		||||
export const DatabaseField: any = observer((props) => {
 | 
			
		||||
@ -33,24 +33,11 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
          console.log({ schema });
 | 
			
		||||
          return (
 | 
			
		||||
            <Collapse.Panel
 | 
			
		||||
              // extra={[
 | 
			
		||||
              //   <Button onClick={() => field.moveUp(index)}>Up</Button>,
 | 
			
		||||
              //   <Button
 | 
			
		||||
              //     onClick={() => {
 | 
			
		||||
              //       field.remove(index);
 | 
			
		||||
              //       field.insert(index, {
 | 
			
		||||
              //         ...item,
 | 
			
		||||
              //         interface: 'textarea',
 | 
			
		||||
              //       });
 | 
			
		||||
              //     }}
 | 
			
		||||
              //   >
 | 
			
		||||
              //     Update
 | 
			
		||||
              //   </Button>,
 | 
			
		||||
              // ]}
 | 
			
		||||
              header={
 | 
			
		||||
                <>
 | 
			
		||||
                  {(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>
 | 
			
		||||
                </>
 | 
			
		||||
              }
 | 
			
		||||
              extra={[
 | 
			
		||||
@ -100,18 +87,24 @@ export const DatabaseField: any = observer((props) => {
 | 
			
		||||
                return;
 | 
			
		||||
              }
 | 
			
		||||
              const data = {
 | 
			
		||||
                ...schema.default,
 | 
			
		||||
                id: uid(),
 | 
			
		||||
                name: uid(),
 | 
			
		||||
                interface: info.key,
 | 
			
		||||
                ...schema.default,
 | 
			
		||||
              };
 | 
			
		||||
              field.push(data);
 | 
			
		||||
              setActiveKey(data.id);
 | 
			
		||||
              console.log('info.key', info.key, schema);
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            {Array.from(interfaces).map(([key, schema]) => (
 | 
			
		||||
              <Menu.Item key={key}>{schema.title}</Menu.Item>
 | 
			
		||||
            {options.map(option => (
 | 
			
		||||
              <Menu.ItemGroup title={option.label}>
 | 
			
		||||
                {
 | 
			
		||||
                  option.children.map(item => (
 | 
			
		||||
                    <Menu.Item key={item.name}>{item.title}</Menu.Item>
 | 
			
		||||
                  ))
 | 
			
		||||
                }
 | 
			
		||||
              </Menu.ItemGroup>
 | 
			
		||||
            ))}
 | 
			
		||||
          </Menu>
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import { ISchema } from '@formily/react';
 | 
			
		||||
import { set } from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { select } from './select';
 | 
			
		||||
import { string } from './string';
 | 
			
		||||
@ -7,7 +8,33 @@ import { textarea } from './textarea';
 | 
			
		||||
 | 
			
		||||
export const interfaces = new Map<string, ISchema>();
 | 
			
		||||
 | 
			
		||||
interfaces.set('select', select);
 | 
			
		||||
interfaces.set('string', string);
 | 
			
		||||
interfaces.set('subTable', subTable);
 | 
			
		||||
interfaces.set('textarea', textarea);
 | 
			
		||||
const fields = {};
 | 
			
		||||
const groupLabels = {};
 | 
			
		||||
 | 
			
		||||
export function registerField(group: string, type: string, schema) {
 | 
			
		||||
  fields[group] = fields[group] || {};
 | 
			
		||||
  set(fields, [group, type], schema);
 | 
			
		||||
  interfaces.set(type, schema);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function registerGroupLabel(key: string, label: string) {
 | 
			
		||||
  groupLabels[key] = label;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
registerField('basic', 'string', string);
 | 
			
		||||
registerField('basic', 'textarea', textarea);
 | 
			
		||||
registerField('choices', 'select', select);
 | 
			
		||||
registerField('relation', 'subTable', subTable);
 | 
			
		||||
 | 
			
		||||
registerGroupLabel('basic', '基本类型');
 | 
			
		||||
registerGroupLabel('choices', '选择类型');
 | 
			
		||||
registerGroupLabel('relation', '关系类型');
 | 
			
		||||
 | 
			
		||||
export const options = Object.keys(fields).map((groupName) => {
 | 
			
		||||
  return {
 | 
			
		||||
    label: groupLabels[groupName],
 | 
			
		||||
    children: Object.keys(fields[groupName]).map((type) => {
 | 
			
		||||
      return fields[groupName][type];
 | 
			
		||||
    }),
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
import { ISchema } from '@formily/react';
 | 
			
		||||
 | 
			
		||||
export const select: ISchema = {
 | 
			
		||||
  name: 'select',
 | 
			
		||||
  type: 'object',
 | 
			
		||||
  title: '下拉选择',
 | 
			
		||||
  group: 'choices',
 | 
			
		||||
  default: {
 | 
			
		||||
    dataType: 'string',
 | 
			
		||||
    // name,
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
import { ISchema } from '@formily/react';
 | 
			
		||||
 | 
			
		||||
export const string: ISchema = {
 | 
			
		||||
  name: 'string',
 | 
			
		||||
  type: 'object',
 | 
			
		||||
  title: '单行文本',
 | 
			
		||||
  group: 'basic',
 | 
			
		||||
  default: {
 | 
			
		||||
    dataType: 'string',
 | 
			
		||||
    // name,
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
import { ISchema } from '@formily/react';
 | 
			
		||||
 | 
			
		||||
export const subTable: ISchema = {
 | 
			
		||||
  name: 'subTable',
 | 
			
		||||
  type: 'object',
 | 
			
		||||
  title: '子表格',
 | 
			
		||||
  group: 'relation',
 | 
			
		||||
  default: {
 | 
			
		||||
    // name,
 | 
			
		||||
    ui: {
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
import { ISchema } from '@formily/react';
 | 
			
		||||
 | 
			
		||||
export const textarea: ISchema = {
 | 
			
		||||
  name: 'textarea',
 | 
			
		||||
  type: 'object',
 | 
			
		||||
  title: '多行文本',
 | 
			
		||||
  group: 'basic',
 | 
			
		||||
  default: {
 | 
			
		||||
    dataType: 'text',
 | 
			
		||||
    // name,
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,5 @@
 | 
			
		||||
import React, { useEffect } from 'react';
 | 
			
		||||
import {
 | 
			
		||||
  connect,
 | 
			
		||||
  mapProps,
 | 
			
		||||
  mapReadPretty,
 | 
			
		||||
} from '@formily/react';
 | 
			
		||||
import { connect, mapProps, mapReadPretty } from '@formily/react';
 | 
			
		||||
import { LoadingOutlined } from '@ant-design/icons';
 | 
			
		||||
import { useDynamicList } from 'ahooks';
 | 
			
		||||
import { Select } from 'antd';
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ import { MenuOutlined, DragOutlined } from '@ant-design/icons';
 | 
			
		||||
import get from 'lodash/get';
 | 
			
		||||
import { GridBlockContext } from '../grid';
 | 
			
		||||
import { uid } from '@formily/shared';
 | 
			
		||||
import { useDesignable } from '../DesignableSchemaField';
 | 
			
		||||
import { useDesignable } from '../';
 | 
			
		||||
import { AddNew } from '../add-new';
 | 
			
		||||
import { BlockItem } from '../block-item';
 | 
			
		||||
import { DraggableBlockContext } from '../../components/drag-and-drop';
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ import {
 | 
			
		||||
  FormProvider,
 | 
			
		||||
  ISchema,
 | 
			
		||||
} from '@formily/react';
 | 
			
		||||
import { SchemaRenderer, useDesignable } from '../DesignableSchemaField';
 | 
			
		||||
import { SchemaRenderer, useDesignable } from '../';
 | 
			
		||||
import get from 'lodash/get';
 | 
			
		||||
import { Dropdown, Menu } from 'antd';
 | 
			
		||||
import {
 | 
			
		||||
@ -56,19 +56,14 @@ export const Form: any = observer((props: any) => {
 | 
			
		||||
  const schema = useFieldSchema();
 | 
			
		||||
  const { schema: designableSchema, refresh } = useDesignable();
 | 
			
		||||
  const formSchema: ISchema = schema['x-decorator'] === 'Form' ? {
 | 
			
		||||
    type: 'void',
 | 
			
		||||
    "x-component": 'Blank',
 | 
			
		||||
    type: 'object',
 | 
			
		||||
    properties: {
 | 
			
		||||
      [schema.name]: {
 | 
			
		||||
        ...schema.toJSON(),
 | 
			
		||||
        "x-decorator": 'Form.Decorator',
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } : {
 | 
			
		||||
    ...schema.toJSON(),
 | 
			
		||||
    'x-component': 'Form.Blank',
 | 
			
		||||
    'x-component-props': {},
 | 
			
		||||
  };
 | 
			
		||||
  } : schema.toJSON();
 | 
			
		||||
  return (
 | 
			
		||||
    <SchemaRenderer
 | 
			
		||||
      scope={scope}
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ import {
 | 
			
		||||
import './style.less';
 | 
			
		||||
import cls from 'classnames';
 | 
			
		||||
 | 
			
		||||
import { useDesignable, useSchemaPath } from '../DesignableSchemaField';
 | 
			
		||||
import { useDesignable, useSchemaPath } from '../';
 | 
			
		||||
import {
 | 
			
		||||
  useDrag,
 | 
			
		||||
  useDrop,
 | 
			
		||||
 | 
			
		||||
@ -1 +1,2 @@
 | 
			
		||||
export * from './DesignableSchemaField';
 | 
			
		||||
// export * from './DesignableSchemaField';
 | 
			
		||||
export * from '../components/schema-renderer';
 | 
			
		||||
 | 
			
		||||
@ -425,6 +425,7 @@ export default () => {
 | 
			
		||||
      <Layout>
 | 
			
		||||
        <Layout.Header>
 | 
			
		||||
          <SchemaRenderer
 | 
			
		||||
            debug={true}
 | 
			
		||||
            schema={schema}
 | 
			
		||||
            scope={{ onSelect, sideMenuRef }}
 | 
			
		||||
          />
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ import { connect, mapReadPretty } from '@formily/react';
 | 
			
		||||
import { Input } from 'antd';
 | 
			
		||||
import { PasswordProps } from 'antd/lib/input';
 | 
			
		||||
import { PasswordStrength } from './PasswordStrength';
 | 
			
		||||
import { Descriptions } from '../descriptions';
 | 
			
		||||
 | 
			
		||||
export interface IPasswordProps extends PasswordProps {
 | 
			
		||||
  checkStrength: boolean;
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ import { LoadingOutlined } from '@ant-design/icons';
 | 
			
		||||
import { SelectProps } from 'antd/lib/select';
 | 
			
		||||
import { isArr, isValid, toArr } from '@formily/shared';
 | 
			
		||||
import { useState } from 'react';
 | 
			
		||||
import { useDesignable } from '../DesignableSchemaField';
 | 
			
		||||
import { useDesignable } from '../';
 | 
			
		||||
import { createContext } from 'react';
 | 
			
		||||
import { useContext } from 'react';
 | 
			
		||||
import { SelectedRowKeysContext, useTableContext } from '../table';
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ import {
 | 
			
		||||
  SortableElement,
 | 
			
		||||
} from 'react-sortable-hoc';
 | 
			
		||||
import cls from 'classnames';
 | 
			
		||||
import { getSchemaPath, useDesignable, useSchemaPath } from '../DesignableSchemaField';
 | 
			
		||||
import { getSchemaPath, useDesignable, useSchemaPath } from '../';
 | 
			
		||||
import './style.less';
 | 
			
		||||
 | 
			
		||||
interface TableRowProps {
 | 
			
		||||
 | 
			
		||||
@ -29,8 +29,8 @@ group:
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { Button } from 'antd'
 | 
			
		||||
import { UploadOutlined, InboxOutlined } from '@ant-design/icons'
 | 
			
		||||
import { SchemaRenderer } from '../';
 | 
			
		||||
import Upload from './';
 | 
			
		||||
import { SchemaRenderer, registerComponents } from '../';
 | 
			
		||||
 | 
			
		||||
const NormalUpload = (props) => {
 | 
			
		||||
  return (
 | 
			
		||||
@ -46,10 +46,6 @@ const NormalUpload = (props) => {
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
registerComponents({
 | 
			
		||||
  NormalUpload,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
  type: 'object',
 | 
			
		||||
  properties: {
 | 
			
		||||
@ -79,7 +75,7 @@ const schema = {
 | 
			
		||||
 | 
			
		||||
export default () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <SchemaRenderer schema={schema} />
 | 
			
		||||
    <SchemaRenderer components={{ NormalUpload }} schema={schema} />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@ -12661,6 +12661,11 @@ moment-timezone@^0.5.31:
 | 
			
		||||
  resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
 | 
			
		||||
  integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
 | 
			
		||||
 | 
			
		||||
monaco-editor@^0.25.2:
 | 
			
		||||
  version "0.25.2"
 | 
			
		||||
  resolved "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.25.2.tgz#119e2b15bbd968a1a99c03cac9c329316d7c37e9"
 | 
			
		||||
  integrity sha512-5iylzSJevCnzJn9UVsW8yOZ3yHjmAs4TfvH3zsbftKiFKmHG0xirGN6DK9Kk04VSWxYCZZAIafYJoNJJMAU1KA==
 | 
			
		||||
 | 
			
		||||
moo@^0.5.0:
 | 
			
		||||
  version "0.5.1"
 | 
			
		||||
  resolved "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user