33 changed files with 816 additions and 1260 deletions
			
			
		- 
					43.eslintrc.cjs
 - 
					6package.json
 - 
					2src/components/cascader/Cascader.tsx
 - 
					21src/components/crazy-form/context.ts
 - 
					222src/components/crazy-form/index.tsx
 - 
					80src/components/crazy-form/tabs-form/TabForm.tsx
 - 
					375src/components/crazy-form/tabs-form/index.tsx
 - 
					74src/components/crazy-form/typeing.d.ts
 - 
					319src/components/x-form/index.tsx
 - 
					13src/components/x-form/style.ts
 - 
					155src/components/x-form/utils/index.tsx
 - 
					19src/context.ts
 - 
					0src/hooks/useNavigate.ts
 - 
					2src/layout/EmptyLayout.tsx
 - 
					31src/layout/RootLayout.tsx
 - 
					2src/pages/app/package/index.tsx
 - 
					16src/pages/system/menus/index.tsx
 - 
					3src/pages/system/roles/index.tsx
 - 
					20src/pages/x-form/hooks/useApi.tsx
 - 
					298src/pages/x-form/index.tsx
 - 
					8src/pages/x-form/utils/index.tsx
 - 
					1src/request.ts
 - 
					33src/routes.tsx
 - 
					4src/service/base.ts
 - 
					6src/service/x-form/model.ts
 - 
					4src/store/app/package.ts
 - 
					3src/store/system/menu.ts
 - 
					259src/store/x-form/model.ts
 - 
					2src/types/x-form/model.d.ts
 - 
					1src/utils/index.ts
 - 
					13src/utils/tree.ts
 - 
					5tsconfig.json
 - 
					34vite.config.ts
 
@ -1,24 +1,25 @@ | 
			
		|||||
module.exports = { | 
				module.exports = { | 
			
		||||
    root: true, | 
				 | 
			
		||||
    env: { browser: true, es2020: true }, | 
				 | 
			
		||||
    extends: [ | 
				 | 
			
		||||
        'eslint:recommended', | 
				 | 
			
		||||
        'plugin:@typescript-eslint/recommended', | 
				 | 
			
		||||
        'plugin:react-hooks/recommended', | 
				 | 
			
		||||
 | 
				  root: true, | 
			
		||||
 | 
				  env: { browser: true, es2020: true }, | 
			
		||||
 | 
				  extends: [ | 
			
		||||
 | 
				    'eslint:recommended', | 
			
		||||
 | 
				    'plugin:@typescript-eslint/recommended', | 
			
		||||
 | 
				    'plugin:react-hooks/recommended', | 
			
		||||
 | 
				  ], | 
			
		||||
 | 
				  ignorePatterns: [ 'dist', '.eslintrc.cjs' ], | 
			
		||||
 | 
				  parser: '@typescript-eslint/parser', | 
			
		||||
 | 
				  plugins: [ 'react-refresh' ], | 
			
		||||
 | 
				  rules: { | 
			
		||||
 | 
				    'react-refresh/only-export-components': [ | 
			
		||||
 | 
				      'warn', | 
			
		||||
 | 
				      { allowConstantExport: true }, | 
			
		||||
    ], | 
				    ], | 
			
		||||
    ignorePatterns: [ 'dist', '.eslintrc.cjs' ], | 
				 | 
			
		||||
    parser: '@typescript-eslint/parser', | 
				 | 
			
		||||
    plugins: [ 'react-refresh' ], | 
				 | 
			
		||||
    rules: { | 
				 | 
			
		||||
        'react-refresh/only-export-components': [ | 
				 | 
			
		||||
            'warn', | 
				 | 
			
		||||
            { allowConstantExport: true }, | 
				 | 
			
		||||
        ], | 
				 | 
			
		||||
        '@typescript-eslint/ban-ts-comment': [ 'error', { | 
				 | 
			
		||||
            'ts-expect-error': 'allow-with-description', | 
				 | 
			
		||||
            'ts-ignore': 'allow-with-description', | 
				 | 
			
		||||
            'minimumDescriptionLength': 10 | 
				 | 
			
		||||
        } ], | 
				 | 
			
		||||
        '@typescript-eslint/no-explicit-any': 'off', | 
				 | 
			
		||||
    }, | 
				 | 
			
		||||
 | 
				    '@typescript-eslint/ban-ts-comment': [ 'error', { | 
			
		||||
 | 
				      'ts-expect-error': 'allow-with-description', | 
			
		||||
 | 
				      'ts-ignore': 'allow-with-description', | 
			
		||||
 | 
				      'minimumDescriptionLength': 10 | 
			
		||||
 | 
				    } ], | 
			
		||||
 | 
				    '@typescript-eslint/no-explicit-any': 'off', | 
			
		||||
 | 
				    'no-unused-vars': 'off', | 
			
		||||
 | 
				  }, | 
			
		||||
} | 
				} | 
			
		||||
@ -1,21 +0,0 @@ | 
			
		|||||
import React from 'react' | 
				 | 
			
		||||
 import { FormInstance } from 'antd/lib' | 
				 | 
			
		||||
import { CrazyChildFormProps } from './typeing' | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
export const CrazyFormProvide = React.createContext< | 
				 | 
			
		||||
        | { | 
				 | 
			
		||||
  regForm: (name: string, props: CrazyChildFormProps<any>) => void; | 
				 | 
			
		||||
  unRegForm: (name: string) => void; | 
				 | 
			
		||||
  onFormFinish: (name: string, formData: any) => void; | 
				 | 
			
		||||
  keyArray: string[]; | 
				 | 
			
		||||
  formArrayRef: React.MutableRefObject< | 
				 | 
			
		||||
          React.MutableRefObject<FormInstance<any> | undefined>[] | 
				 | 
			
		||||
  >; | 
				 | 
			
		||||
  loading: boolean; | 
				 | 
			
		||||
  setLoading: (loading: boolean) => void; | 
				 | 
			
		||||
  formMapRef: React.MutableRefObject<Map<string, CrazyChildFormProps>>; | 
				 | 
			
		||||
} | 
				 | 
			
		||||
        | undefined | 
				 | 
			
		||||
>(undefined) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
@ -1,222 +0,0 @@ | 
			
		|||||
import { | 
				 | 
			
		||||
  FormSchema, | 
				 | 
			
		||||
  ItemType, | 
				 | 
			
		||||
  ProFormRenderValueTypeHelpers | 
				 | 
			
		||||
} from '@ant-design/pro-form/es/components/SchemaForm/typing' | 
				 | 
			
		||||
import { StepsForm, Embed } from '@ant-design/pro-form/es/components/SchemaForm/layoutType' | 
				 | 
			
		||||
import { renderValueType } from '@ant-design/pro-form/es/components/SchemaForm/valueType' | 
				 | 
			
		||||
import { | 
				 | 
			
		||||
  DrawerForm, FormProps, LabelIconTip, | 
				 | 
			
		||||
  LightFilter, | 
				 | 
			
		||||
  ModalForm, omitUndefined, | 
				 | 
			
		||||
  ProForm, ProFormColumnsType, ProFormInstance, ProFormProps, | 
				 | 
			
		||||
  QueryFilter, runFunction, | 
				 | 
			
		||||
  StepsForm as ProStepsForm, stringify, useDeepCompareMemo, useLatest, useReactiveRef, useRefFunction | 
				 | 
			
		||||
} from '@ant-design/pro-components' | 
				 | 
			
		||||
import React, { useCallback, useImperativeHandle, useRef, useState } from 'react' | 
				 | 
			
		||||
import { TabsForm } from './tabs-form' | 
				 | 
			
		||||
import { Form } from 'antd' | 
				 | 
			
		||||
 | 
				 | 
			
		||||
export type CrazyBateFormProps<T, ValueType> = { | 
				 | 
			
		||||
  layoutType: FormSchema<T, ValueType>['layoutType'] | 'TabsForm' | 
				 | 
			
		||||
} & Omit<FormSchema<T, ValueType>, 'layout'> | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
const FormLayoutType = { | 
				 | 
			
		||||
  DrawerForm, | 
				 | 
			
		||||
  QueryFilter, | 
				 | 
			
		||||
  LightFilter, StepForm: ProStepsForm.StepForm, | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  StepsForm: StepsForm, | 
				 | 
			
		||||
  ModalForm, | 
				 | 
			
		||||
  Embed, | 
				 | 
			
		||||
  Form: ProForm, | 
				 | 
			
		||||
  TabsForm, | 
				 | 
			
		||||
} | 
				 | 
			
		||||
 | 
				 | 
			
		||||
const CrazyBateForm = <T, ValueType = 'text'>(props: CrazyBateFormProps<T, ValueType>) => { | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const { | 
				 | 
			
		||||
    columns, | 
				 | 
			
		||||
    layoutType = 'TabsForm', | 
				 | 
			
		||||
    type = 'form', | 
				 | 
			
		||||
    action, | 
				 | 
			
		||||
    shouldUpdate = (pre, next) => stringify(pre) !== stringify(next), | 
				 | 
			
		||||
    formRef: propsFormRef, | 
				 | 
			
		||||
    ...restProps | 
				 | 
			
		||||
  } = props | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const FormRenderComponents = (FormLayoutType[layoutType as 'TabsForm'] || | 
				 | 
			
		||||
          ProForm) as React.FC<ProFormProps<T>> | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const [ form ] = Form.useForm() | 
				 | 
			
		||||
  const formInstance = Form.useFormInstance() | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const [ , forceUpdate ] = useState<[]>([]) | 
				 | 
			
		||||
  const [ formDomsDeps, updatedFormDoms ] = useState<[]>(() => []) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const formRef = useReactiveRef<ProFormInstance | undefined>( | 
				 | 
			
		||||
          props.form || formInstance || form, | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
  const oldValuesRef = useRef<T>() | 
				 | 
			
		||||
  const propsRef = useLatest(props) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  /** | 
				 | 
			
		||||
   * 生成子项,方便被 table 接入 | 
				 | 
			
		||||
   * | 
				 | 
			
		||||
   * @param items | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  const genItems: ProFormRenderValueTypeHelpers<T, ValueType>['genItems'] = | 
				 | 
			
		||||
          useRefFunction((items: ProFormColumnsType<T, ValueType>[]) => { | 
				 | 
			
		||||
            return items | 
				 | 
			
		||||
                    .filter((originItem) => { | 
				 | 
			
		||||
                      return !(originItem.hideInForm && type === 'form') | 
				 | 
			
		||||
                    }) | 
				 | 
			
		||||
                    .sort((a, b) => { | 
				 | 
			
		||||
                      if (b.order || a.order) { | 
				 | 
			
		||||
                        return (b.order || 0) - (a.order || 0) | 
				 | 
			
		||||
                      } | 
				 | 
			
		||||
                      return (b.index || 0) - (a.index || 0) | 
				 | 
			
		||||
                    }) | 
				 | 
			
		||||
                    .map((originItem, index) => { | 
				 | 
			
		||||
                      const title = runFunction( | 
				 | 
			
		||||
                              originItem.title, | 
				 | 
			
		||||
                              originItem, | 
				 | 
			
		||||
                              'form', | 
				 | 
			
		||||
                              <LabelIconTip | 
				 | 
			
		||||
                                      label={originItem.title as string} | 
				 | 
			
		||||
                                      //@ts-ignore @ts-expect-error
 | 
				 | 
			
		||||
                                      tooltip={originItem.tooltip || originItem.tip} | 
				 | 
			
		||||
                              />, | 
				 | 
			
		||||
                      ) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
                      const item = omitUndefined({ | 
				 | 
			
		||||
                        title, | 
				 | 
			
		||||
                        label: title, | 
				 | 
			
		||||
                        name: originItem.name, | 
				 | 
			
		||||
                        valueType: runFunction(originItem.valueType, {}), | 
				 | 
			
		||||
                        key: originItem.key || originItem.dataIndex || index, | 
				 | 
			
		||||
                        columns: originItem.columns, | 
				 | 
			
		||||
                        valueEnum: originItem.valueEnum, | 
				 | 
			
		||||
                        dataIndex: originItem.dataIndex || originItem.key, | 
				 | 
			
		||||
                        initialValue: originItem.initialValue, | 
				 | 
			
		||||
                        width: originItem.width, | 
				 | 
			
		||||
                        index: originItem.index, | 
				 | 
			
		||||
                        readonly: originItem.readonly, | 
				 | 
			
		||||
                        colSize: originItem.colSize, | 
				 | 
			
		||||
                        colProps: originItem.colProps, | 
				 | 
			
		||||
                        rowProps: originItem.rowProps, | 
				 | 
			
		||||
                        className: originItem.className, | 
				 | 
			
		||||
                        //@ts-ignore  @ts-expect-error
 | 
				 | 
			
		||||
                        tooltip: originItem.tooltip || originItem.tip, | 
				 | 
			
		||||
                        dependencies: originItem.dependencies, | 
				 | 
			
		||||
                        proFieldProps: originItem.proFieldProps, | 
				 | 
			
		||||
                        ignoreFormItem: originItem.ignoreFormItem, | 
				 | 
			
		||||
                        getFieldProps: originItem.fieldProps | 
				 | 
			
		||||
                                ? () => | 
				 | 
			
		||||
                                        runFunction( | 
				 | 
			
		||||
                                                originItem.fieldProps, | 
				 | 
			
		||||
                                                formRef.current, | 
				 | 
			
		||||
                                                originItem, | 
				 | 
			
		||||
                                        ) | 
				 | 
			
		||||
                                : undefined, | 
				 | 
			
		||||
                        getFormItemProps: originItem.formItemProps | 
				 | 
			
		||||
                                ? () => | 
				 | 
			
		||||
                                        runFunction( | 
				 | 
			
		||||
                                                originItem.formItemProps, | 
				 | 
			
		||||
                                                formRef.current, | 
				 | 
			
		||||
                                                originItem, | 
				 | 
			
		||||
                                        ) | 
				 | 
			
		||||
                                : undefined, | 
				 | 
			
		||||
                        render: originItem.render, | 
				 | 
			
		||||
                        renderFormItem: originItem.renderFormItem, | 
				 | 
			
		||||
                        renderText: originItem.renderText, | 
				 | 
			
		||||
                        request: originItem.request, | 
				 | 
			
		||||
                        params: originItem.params, | 
				 | 
			
		||||
                        transform: originItem.transform, | 
				 | 
			
		||||
                        convertValue: originItem.convertValue, | 
				 | 
			
		||||
                        debounceTime: originItem.debounceTime, | 
				 | 
			
		||||
                        defaultKeyWords: originItem.defaultKeyWords, | 
				 | 
			
		||||
                      }) as ItemType<any, any> | 
				 | 
			
		||||
 | 
				 | 
			
		||||
                      return renderValueType(item, { | 
				 | 
			
		||||
                        action, | 
				 | 
			
		||||
                        type, | 
				 | 
			
		||||
                        originItem, | 
				 | 
			
		||||
                        formRef, | 
				 | 
			
		||||
                        genItems, | 
				 | 
			
		||||
                      }) | 
				 | 
			
		||||
                    }) | 
				 | 
			
		||||
                    .filter((field) => { | 
				 | 
			
		||||
                      return Boolean(field) | 
				 | 
			
		||||
                    }) | 
				 | 
			
		||||
          }) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const onValuesChange: FormProps<T>['onValuesChange'] = useCallback( | 
				 | 
			
		||||
          (changedValues: any, values: T) => { | 
				 | 
			
		||||
            const { onValuesChange: propsOnValuesChange } = propsRef.current | 
				 | 
			
		||||
            if ( | 
				 | 
			
		||||
                    shouldUpdate === true || | 
				 | 
			
		||||
                    (typeof shouldUpdate === 'function' && | 
				 | 
			
		||||
                            shouldUpdate(values, oldValuesRef.current)) | 
				 | 
			
		||||
            ) { | 
				 | 
			
		||||
              updatedFormDoms([]) | 
				 | 
			
		||||
            } | 
				 | 
			
		||||
            oldValuesRef.current = values | 
				 | 
			
		||||
            propsOnValuesChange?.(changedValues, values) | 
				 | 
			
		||||
          }, | 
				 | 
			
		||||
          [ propsRef, shouldUpdate ], | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const formChildrenDoms = useDeepCompareMemo(() => { | 
				 | 
			
		||||
    if (!formRef.current) return | 
				 | 
			
		||||
    // like StepsForm's columns but not only for StepsForm
 | 
				 | 
			
		||||
    if (columns.length && Array.isArray(columns[0])) return | 
				 | 
			
		||||
    return genItems(columns as ProFormColumnsType<T, ValueType>[]) | 
				 | 
			
		||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
				 | 
			
		||||
  }, [ columns, restProps?.open, action, type, formDomsDeps, !!formRef.current ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  /** | 
				 | 
			
		||||
   * Append layoutType component specific props | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  const specificProps = useDeepCompareMemo(() => { | 
				 | 
			
		||||
    if (layoutType === 'StepsForm') { | 
				 | 
			
		||||
      return { | 
				 | 
			
		||||
        forceUpdate: forceUpdate, | 
				 | 
			
		||||
        columns: columns as ProFormColumnsType<T, ValueType>[][], | 
				 | 
			
		||||
      } | 
				 | 
			
		||||
    } | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    return {} | 
				 | 
			
		||||
  }, [ columns, layoutType ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  useImperativeHandle( | 
				 | 
			
		||||
          propsFormRef, | 
				 | 
			
		||||
          () => { | 
				 | 
			
		||||
            return formRef.current | 
				 | 
			
		||||
          }, | 
				 | 
			
		||||
          [ formRef.current ], | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  return ( | 
				 | 
			
		||||
          <FormRenderComponents | 
				 | 
			
		||||
                  {...specificProps} | 
				 | 
			
		||||
                  {...restProps} | 
				 | 
			
		||||
                  onInit={(_, initForm) => { | 
				 | 
			
		||||
                    if (propsFormRef) { | 
				 | 
			
		||||
                      (propsFormRef as React.MutableRefObject<ProFormInstance<T>>).current = | 
				 | 
			
		||||
                              initForm | 
				 | 
			
		||||
                    } | 
				 | 
			
		||||
                    restProps?.onInit?.(_, initForm) | 
				 | 
			
		||||
                    formRef.current = initForm | 
				 | 
			
		||||
                  }} | 
				 | 
			
		||||
                  form={props.form || form} | 
				 | 
			
		||||
                  formRef={formRef} | 
				 | 
			
		||||
                  onValuesChange={onValuesChange} | 
				 | 
			
		||||
          > | 
				 | 
			
		||||
            {formChildrenDoms} | 
				 | 
			
		||||
          </FormRenderComponents> | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
} | 
				 | 
			
		||||
 | 
				 | 
			
		||||
export default CrazyBateForm | 
				 | 
			
		||||
@ -1,80 +0,0 @@ | 
			
		|||||
import { useContext, useEffect, useImperativeHandle, useRef } from 'react' | 
				 | 
			
		||||
import { TabsProps } from 'antd' | 
				 | 
			
		||||
import { noteOnce } from 'rc-util/lib/warning' | 
				 | 
			
		||||
import { FormInstance } from 'antd/lib' | 
				 | 
			
		||||
import { CrazyFormProvide } from '../context.ts' | 
				 | 
			
		||||
import { TabFormProvide } from './index.tsx' | 
				 | 
			
		||||
import { CrazyChildFormProps } from '../typeing' | 
				 | 
			
		||||
 | 
				 | 
			
		||||
export type TabFormProps<T = Record<string, any>> = { | 
				 | 
			
		||||
  tab?: string; | 
				 | 
			
		||||
  tabProps?: TabsProps; | 
				 | 
			
		||||
} & CrazyChildFormProps<T> | 
				 | 
			
		||||
 | 
				 | 
			
		||||
const TabForm = <T = Record<string, any>>(props: TabFormProps<T>) => { | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const formRef = useRef<FormInstance | undefined>() | 
				 | 
			
		||||
  const context = useContext(CrazyFormProvide) | 
				 | 
			
		||||
  const tabContext = useContext(TabFormProvide) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const { | 
				 | 
			
		||||
    onFinish, | 
				 | 
			
		||||
    tab, | 
				 | 
			
		||||
    formRef: propFormRef, | 
				 | 
			
		||||
    tabProps, | 
				 | 
			
		||||
    ...restProps | 
				 | 
			
		||||
  } = props | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  noteOnce(!(restProps as any).submitter, 'TabForm 不包含提交按钮,请在 TabsForm 上') | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  /** 重置 formRef */ | 
				 | 
			
		||||
  useImperativeHandle(propFormRef, () => formRef.current, [ | 
				 | 
			
		||||
    propFormRef?.current, | 
				 | 
			
		||||
  ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  /** Dom 不存在的时候解除挂载 */ | 
				 | 
			
		||||
  useEffect(() => { | 
				 | 
			
		||||
    if (!(props.name || props.tab)) return | 
				 | 
			
		||||
    const name = (props.name || props.tab)!.toString() | 
				 | 
			
		||||
    context?.regForm(name, props) | 
				 | 
			
		||||
    return () => { | 
				 | 
			
		||||
      context?.unRegForm(name) | 
				 | 
			
		||||
    } | 
				 | 
			
		||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
				 | 
			
		||||
  }, []) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  if (context && context?.formArrayRef) { | 
				 | 
			
		||||
    context.formArrayRef.current[tab || 0] = formRef | 
				 | 
			
		||||
  } | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  return ( | 
				 | 
			
		||||
          <BaseForm | 
				 | 
			
		||||
                  formRef={formRef} | 
				 | 
			
		||||
                  onFinish={async (values) => { | 
				 | 
			
		||||
                    if (restProps.name) { | 
				 | 
			
		||||
                      context?.onFormFinish(restProps.name, values) | 
				 | 
			
		||||
                    } | 
				 | 
			
		||||
                    if (onFinish) { | 
				 | 
			
		||||
                      context?.setLoading(true) | 
				 | 
			
		||||
                      // 如果报错,直接抛出
 | 
				 | 
			
		||||
                      await onFinish?.(values) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
                      context?.setLoading(false) | 
				 | 
			
		||||
                      return | 
				 | 
			
		||||
                    } | 
				 | 
			
		||||
 | 
				 | 
			
		||||
                  }} | 
				 | 
			
		||||
                  onInit={(_, form) => { | 
				 | 
			
		||||
                    formRef.current = form | 
				 | 
			
		||||
                    if (context && context?.formArrayRef) { | 
				 | 
			
		||||
                      context.formArrayRef.current[tab || 0] = formRef | 
				 | 
			
		||||
                    } | 
				 | 
			
		||||
                    restProps?.onInit?.(_, form) | 
				 | 
			
		||||
                  }} | 
				 | 
			
		||||
                  layout="vertical" | 
				 | 
			
		||||
                  {...omit(restProps, [ 'layoutType', 'columns' ] as any[])} | 
				 | 
			
		||||
          /> | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
} | 
				 | 
			
		||||
 | 
				 | 
			
		||||
export default TabForm | 
				 | 
			
		||||
@ -1,375 +0,0 @@ | 
			
		|||||
import React, { useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react' | 
				 | 
			
		||||
import TabForm, { TabFormProps } from './TabForm' | 
				 | 
			
		||||
import { CrazyFormProps } from '../typeing' | 
				 | 
			
		||||
import { Button, Col, Form, Row, Space, Tabs, TabsProps } from 'antd' | 
				 | 
			
		||||
import { FormInstance } from 'antd/lib' | 
				 | 
			
		||||
import toArray from 'rc-util/lib/Children/toArray' | 
				 | 
			
		||||
import useMergedState from 'rc-util/lib/hooks/useMergedState' | 
				 | 
			
		||||
import { merge, ProConfigProvider, useRefFunction } from '@ant-design/pro-components' | 
				 | 
			
		||||
import { t } from '@/i18n' | 
				 | 
			
		||||
import classnames from 'classnames' | 
				 | 
			
		||||
import { CrazyFormProvide } from '../context' | 
				 | 
			
		||||
 | 
				 | 
			
		||||
type TabsFormProps<T = Record<string, any>> = { | 
				 | 
			
		||||
  toggleProps: TabsProps | 
				 | 
			
		||||
  direction?: 'horizontal' | 'vertical' | 
				 | 
			
		||||
} & Omit<CrazyFormProps<T>, 'toggleProps'> | 
				 | 
			
		||||
 | 
				 | 
			
		||||
export const TabFormProvide = React.createContext<TabFormProps<any> | null>(null) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
const TabsLayoutStrategy: Record< | 
				 | 
			
		||||
        string, | 
				 | 
			
		||||
        (dom: LayoutRenderDom) => React.ReactNode | 
				 | 
			
		||||
> = { | 
				 | 
			
		||||
  horizontal({ toggleDom, formDom }) { | 
				 | 
			
		||||
    return ( | 
				 | 
			
		||||
            <> | 
				 | 
			
		||||
              <Row gutter={{ xs: 8, sm: 16, md: 24 }}> | 
				 | 
			
		||||
                <Col span={24}>{toggleDom}</Col> | 
				 | 
			
		||||
              </Row> | 
				 | 
			
		||||
              <Row gutter={{ xs: 8, sm: 16, md: 24 }}> | 
				 | 
			
		||||
                <Col span={24}>{formDom}</Col> | 
				 | 
			
		||||
              </Row> | 
				 | 
			
		||||
            </> | 
				 | 
			
		||||
    ) | 
				 | 
			
		||||
  }, | 
				 | 
			
		||||
  vertical({ stepsDom, formDom }) { | 
				 | 
			
		||||
    return ( | 
				 | 
			
		||||
            <Row align="stretch" wrap={true} gutter={{ xs: 8, sm: 16, md: 24 }}> | 
				 | 
			
		||||
              <Col xxl={4} xl={6} lg={7} md={8} sm={10} xs={12}> | 
				 | 
			
		||||
                {React.cloneElement(stepsDom, { | 
				 | 
			
		||||
                  style: { | 
				 | 
			
		||||
                    height: '100%', | 
				 | 
			
		||||
                  }, | 
				 | 
			
		||||
                })} | 
				 | 
			
		||||
              </Col> | 
				 | 
			
		||||
              <Col> | 
				 | 
			
		||||
                <div | 
				 | 
			
		||||
                        style={{ | 
				 | 
			
		||||
                          display: 'flex', | 
				 | 
			
		||||
                          alignItems: 'center', | 
				 | 
			
		||||
                          width: '100%', | 
				 | 
			
		||||
                          height: '100%', | 
				 | 
			
		||||
                        }} | 
				 | 
			
		||||
                > | 
				 | 
			
		||||
                  {formDom} | 
				 | 
			
		||||
                </div> | 
				 | 
			
		||||
              </Col> | 
				 | 
			
		||||
            </Row> | 
				 | 
			
		||||
    ) | 
				 | 
			
		||||
  }, | 
				 | 
			
		||||
} | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
const TabsForm = <T = Record<string, any>>( | 
				 | 
			
		||||
        props: TabsFormProps<T> & { | 
				 | 
			
		||||
          children: React.ReactNode | 
				 | 
			
		||||
        } | 
				 | 
			
		||||
) => { | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const { | 
				 | 
			
		||||
    toggleProps, | 
				 | 
			
		||||
    toggleFormRender, | 
				 | 
			
		||||
    direction = 'horizontal', | 
				 | 
			
		||||
    current: tab, | 
				 | 
			
		||||
    onCurrentChange, | 
				 | 
			
		||||
    submitter, | 
				 | 
			
		||||
    formRender, | 
				 | 
			
		||||
    onFinish, | 
				 | 
			
		||||
    formProps, | 
				 | 
			
		||||
    containerStyle, | 
				 | 
			
		||||
    formRef, | 
				 | 
			
		||||
    formMapRef: propsFormMapRef, | 
				 | 
			
		||||
    layoutRender: propsLayoutRender, | 
				 | 
			
		||||
    ...rest | 
				 | 
			
		||||
  } = props | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const formDataRef = useRef(new Map<string, Record<string, any>>()) | 
				 | 
			
		||||
  const formMapRef = useRef(new Map<string, TabFormProps>()) | 
				 | 
			
		||||
  const formArrayRef = useRef< | 
				 | 
			
		||||
          React.MutableRefObject<FormInstance<any> | undefined>[] | 
				 | 
			
		||||
  >([]) | 
				 | 
			
		||||
  const [ formArray, setFormArray ] = useState<string[]>([]) | 
				 | 
			
		||||
  const [ loading, setLoading ] = useState<boolean>(false) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  /** | 
				 | 
			
		||||
   * 受控的方式来操作表单 | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  const [ tab, setTab ] = useMergedState<number>(0, { | 
				 | 
			
		||||
    value: props.current, | 
				 | 
			
		||||
    onChange: props.onCurrentChange, | 
				 | 
			
		||||
  }) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const layoutRender = useMemo(() => { | 
				 | 
			
		||||
    return TabsLayoutStrategy[direction] | 
				 | 
			
		||||
  }, [ direction ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  /** | 
				 | 
			
		||||
   * 注册一个form进入,方便进行 props 的修改 | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  const regForm = useCallback( | 
				 | 
			
		||||
          (name: string, childrenFormProps: TabFormProps) => { | 
				 | 
			
		||||
            if (!formMapRef.current.has(name)) { | 
				 | 
			
		||||
              setFormArray((oldFormArray) => [ ...oldFormArray, name ]) | 
				 | 
			
		||||
            } | 
				 | 
			
		||||
            formMapRef.current.set(name, childrenFormProps) | 
				 | 
			
		||||
          }, | 
				 | 
			
		||||
          [], | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  /** | 
				 | 
			
		||||
   * 解除挂载掉这个 form,同时步数 -1 | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  const unRegForm = useCallback((name: string) => { | 
				 | 
			
		||||
    setFormArray((oldFormArray) => oldFormArray.filter((n) => n !== name)) | 
				 | 
			
		||||
    formMapRef.current.delete(name) | 
				 | 
			
		||||
    formDataRef.current.delete(name) | 
				 | 
			
		||||
  }, []) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  useImperativeHandle(propsFormMapRef, () => formArrayRef.current, [ | 
				 | 
			
		||||
    formArrayRef.current, | 
				 | 
			
		||||
  ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  useImperativeHandle( | 
				 | 
			
		||||
          formRef, | 
				 | 
			
		||||
          () => { | 
				 | 
			
		||||
            return formArrayRef.current[tab || 0]?.current | 
				 | 
			
		||||
          }, | 
				 | 
			
		||||
          [ tab, formArrayRef.current ], | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  /** | 
				 | 
			
		||||
   * ProForm处理了一下 from 的数据,在其中做了一些操作 如果使用 Provider 自带的,自带的数据处理就无法生效了 | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  const onFormFinish = useCallback( | 
				 | 
			
		||||
          async (name: string, formData: any) => { | 
				 | 
			
		||||
            formDataRef.current.set(name, formData) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
            setLoading(true) | 
				 | 
			
		||||
            const values: any = merge( | 
				 | 
			
		||||
                    {}, | 
				 | 
			
		||||
                    ...Array.from(formDataRef.current.values()), | 
				 | 
			
		||||
            ) | 
				 | 
			
		||||
            try { | 
				 | 
			
		||||
              const success = await onFinish(values) | 
				 | 
			
		||||
              if (success) { | 
				 | 
			
		||||
                formArrayRef.current.forEach((form) => form.current?.resetFields()) | 
				 | 
			
		||||
              } | 
				 | 
			
		||||
            } catch (error) { | 
				 | 
			
		||||
              console.log(error) | 
				 | 
			
		||||
            } finally { | 
				 | 
			
		||||
              setLoading(false) | 
				 | 
			
		||||
            } | 
				 | 
			
		||||
          }, | 
				 | 
			
		||||
          [ lastStep, onFinish, setLoading, setTab ], | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const toggleDoms = useMemo(() => { | 
				 | 
			
		||||
    const itemsProps = { | 
				 | 
			
		||||
      items: formArray.map((item) => { | 
				 | 
			
		||||
        const itemProps = formMapRef.current.get(item) | 
				 | 
			
		||||
        return { | 
				 | 
			
		||||
          key: item, | 
				 | 
			
		||||
          title: itemProps?.title, | 
				 | 
			
		||||
          ...itemProps?.tabProps, | 
				 | 
			
		||||
        } | 
				 | 
			
		||||
      }), | 
				 | 
			
		||||
    } | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    return ( | 
				 | 
			
		||||
            <div className={`crazy-tabs-container`.trim()} | 
				 | 
			
		||||
 | 
				 | 
			
		||||
            > | 
				 | 
			
		||||
              <Tabs | 
				 | 
			
		||||
                      {...toggleProps} | 
				 | 
			
		||||
                      {...itemsProps} | 
				 | 
			
		||||
                      activeKey={tab} | 
				 | 
			
		||||
                      onChange={onCurrentChange} | 
				 | 
			
		||||
              > | 
				 | 
			
		||||
              </Tabs> | 
				 | 
			
		||||
            </div> | 
				 | 
			
		||||
    ) | 
				 | 
			
		||||
  }, [ formArray, tab, toggleProps, onCurrentChange ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const onSubmit = useRefFunction(() => { | 
				 | 
			
		||||
    const from = formArrayRef.current[tab] | 
				 | 
			
		||||
    from.current?.submit() | 
				 | 
			
		||||
  }) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const submit = useMemo(() => { | 
				 | 
			
		||||
    return ( | 
				 | 
			
		||||
            submitter !== false && ( | 
				 | 
			
		||||
                    <Button | 
				 | 
			
		||||
                            key="submit" | 
				 | 
			
		||||
                            type="primary" | 
				 | 
			
		||||
                            loading={loading} | 
				 | 
			
		||||
                            {...submitter?.submitButtonProps} | 
				 | 
			
		||||
                            onClick={() => { | 
				 | 
			
		||||
                              submitter?.onSubmit?.() | 
				 | 
			
		||||
                              onSubmit() | 
				 | 
			
		||||
                            }} | 
				 | 
			
		||||
                    > | 
				 | 
			
		||||
                      {t('actions.submit', '提交')} | 
				 | 
			
		||||
                    </Button> | 
				 | 
			
		||||
            ) | 
				 | 
			
		||||
    ) | 
				 | 
			
		||||
  }, [ loading, onSubmit, submitter ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const submitterDom = useMemo(() => { | 
				 | 
			
		||||
    let buttons: (React.ReactElement | false)[] = [ submit ] | 
				 | 
			
		||||
    buttons = buttons.filter(React.isValidElement) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    if (submitter && submitter.render) { | 
				 | 
			
		||||
      const submitterProps: any = { | 
				 | 
			
		||||
        form: formArrayRef.current[tab]?.current, | 
				 | 
			
		||||
        onSubmit, | 
				 | 
			
		||||
        current: tab, | 
				 | 
			
		||||
      } | 
				 | 
			
		||||
 | 
				 | 
			
		||||
      return submitter.render( | 
				 | 
			
		||||
              submitterProps, | 
				 | 
			
		||||
              buttons as React.ReactElement[], | 
				 | 
			
		||||
      ) as React.ReactNode | 
				 | 
			
		||||
    } | 
				 | 
			
		||||
    if (submitter && submitter?.render === false) { | 
				 | 
			
		||||
      return null | 
				 | 
			
		||||
    } | 
				 | 
			
		||||
    return buttons as React.ReactElement[] | 
				 | 
			
		||||
  }, [ formArray.length, onSubmit, tab, submit, submitter ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const formDom = useMemo(() => { | 
				 | 
			
		||||
    return toArray(props.children).map((item, index) => { | 
				 | 
			
		||||
      const itemProps = item.props as TabFormProps | 
				 | 
			
		||||
      const name = itemProps.name || `${index}` | 
				 | 
			
		||||
      /** 是否是当前的表单 */ | 
				 | 
			
		||||
      const isShow = tab === name | 
				 | 
			
		||||
 | 
				 | 
			
		||||
      const config = isShow | 
				 | 
			
		||||
              ? { | 
				 | 
			
		||||
                contentRender: formRender, | 
				 | 
			
		||||
              } | 
				 | 
			
		||||
              : {} | 
				 | 
			
		||||
      return ( | 
				 | 
			
		||||
              <div | 
				 | 
			
		||||
                      className={classnames(`crazy-tab`, { | 
				 | 
			
		||||
                        [`crazy-tab-active`]: isShow, | 
				 | 
			
		||||
                      })} | 
				 | 
			
		||||
                      key={name} | 
				 | 
			
		||||
              > | 
				 | 
			
		||||
                <TabFormProvide.Provider | 
				 | 
			
		||||
                        value={{ | 
				 | 
			
		||||
                          ...config, | 
				 | 
			
		||||
                          ...formProps, | 
				 | 
			
		||||
                          ...itemProps, | 
				 | 
			
		||||
                          name, | 
				 | 
			
		||||
                          index, | 
				 | 
			
		||||
                          tab: name, | 
				 | 
			
		||||
                        }} | 
				 | 
			
		||||
                > | 
				 | 
			
		||||
                  {item} | 
				 | 
			
		||||
                </TabFormProvide.Provider> | 
				 | 
			
		||||
              </div> | 
				 | 
			
		||||
      ) | 
				 | 
			
		||||
    }) | 
				 | 
			
		||||
  }, [ formProps, props.children, tab, formRender ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const finalTabsDom = useMemo(() => { | 
				 | 
			
		||||
    if (toggleFormRender) { | 
				 | 
			
		||||
      return toggleFormRender( | 
				 | 
			
		||||
              formArray.map((item) => ({ | 
				 | 
			
		||||
                key: item, | 
				 | 
			
		||||
                title: formMapRef.current.get(item)?.title, | 
				 | 
			
		||||
              })), | 
				 | 
			
		||||
              toggleDoms, | 
				 | 
			
		||||
      ) as React.ReactElement | 
				 | 
			
		||||
    } | 
				 | 
			
		||||
    return toggleDoms | 
				 | 
			
		||||
  }, [ formArray, toggleDoms, toggleFormRender ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const formContainer = useMemo( | 
				 | 
			
		||||
          () => ( | 
				 | 
			
		||||
                  <div | 
				 | 
			
		||||
                          className={`crazy-container`.trim()} | 
				 | 
			
		||||
                          style={containerStyle} | 
				 | 
			
		||||
                  > | 
				 | 
			
		||||
                    {formDom} | 
				 | 
			
		||||
                    {toggleFormRender ? null : <Space>{submitterDom}</Space>} | 
				 | 
			
		||||
                  </div> | 
				 | 
			
		||||
          ), | 
				 | 
			
		||||
          [ containerStyle, formDom, toggleFormRender, submitterDom ], | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  const tabsFormDom = useMemo(() => { | 
				 | 
			
		||||
    const doms = { | 
				 | 
			
		||||
      toggleDom: finalTabsDom, | 
				 | 
			
		||||
      formDom: formContainer, | 
				 | 
			
		||||
    } | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    if (toggleFormRender) { | 
				 | 
			
		||||
      if (propsLayoutRender) { | 
				 | 
			
		||||
        return toggleFormRender(propsLayoutRender(doms), submitterDom) | 
				 | 
			
		||||
      } else { | 
				 | 
			
		||||
        return toggleFormRender(layoutRender(doms), submitterDom) | 
				 | 
			
		||||
      } | 
				 | 
			
		||||
    } | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    if (propsLayoutRender) { | 
				 | 
			
		||||
      return propsLayoutRender(doms) | 
				 | 
			
		||||
    } | 
				 | 
			
		||||
 | 
				 | 
			
		||||
    return layoutRender(doms) | 
				 | 
			
		||||
  }, [ | 
				 | 
			
		||||
    finalTabsDom, | 
				 | 
			
		||||
    formContainer, | 
				 | 
			
		||||
    layoutRender, | 
				 | 
			
		||||
    toggleFormRender, | 
				 | 
			
		||||
    submitterDom, | 
				 | 
			
		||||
    propsLayoutRender, | 
				 | 
			
		||||
  ]) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  return ( | 
				 | 
			
		||||
          <div> | 
				 | 
			
		||||
            <Form.Provider {...rest}> | 
				 | 
			
		||||
              <CrazyFormProvide.Provider | 
				 | 
			
		||||
                      value={{ | 
				 | 
			
		||||
                        loading, | 
				 | 
			
		||||
                        setLoading, | 
				 | 
			
		||||
                        regForm, | 
				 | 
			
		||||
                        keyArray: formArray, | 
				 | 
			
		||||
                        formArrayRef, | 
				 | 
			
		||||
                        formMapRef, | 
				 | 
			
		||||
                        unRegForm, | 
				 | 
			
		||||
                        onFormFinish, | 
				 | 
			
		||||
                      }} | 
				 | 
			
		||||
              > | 
				 | 
			
		||||
                {tabsFormDom} | 
				 | 
			
		||||
              </CrazyFormProvide.Provider> | 
				 | 
			
		||||
            </Form.Provider> | 
				 | 
			
		||||
          </div> | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
 | 
				 | 
			
		||||
} | 
				 | 
			
		||||
 | 
				 | 
			
		||||
function TabsFormWarp<T = Record<string, any>>( | 
				 | 
			
		||||
        props: CrazyFormProps<T> & { | 
				 | 
			
		||||
          children: any; | 
				 | 
			
		||||
        }, | 
				 | 
			
		||||
) { | 
				 | 
			
		||||
  return ( | 
				 | 
			
		||||
          <ProConfigProvider needDeps> | 
				 | 
			
		||||
            <TabsForm<T> {...props} /> | 
				 | 
			
		||||
          </ProConfigProvider> | 
				 | 
			
		||||
  ) | 
				 | 
			
		||||
} | 
				 | 
			
		||||
 | 
				 | 
			
		||||
TabsFormWarp.TabForm = TabForm | 
				 | 
			
		||||
TabsFormWarp.useForm = Form.useForm | 
				 | 
			
		||||
 | 
				 | 
			
		||||
export { TabsFormWarp as TabsForm } | 
				 | 
			
		||||
export type { TabFormProps, CrazyFormProps as TabsFormProps } | 
				 | 
			
		||||
@ -1,74 +0,0 @@ | 
			
		|||||
import { FormInstance } from 'antd/lib' | 
				 | 
			
		||||
import { FormProps, ProFormInstance, ProFormProps, SubmitterProps } from '@ant-design/pro-components' | 
				 | 
			
		||||
import React from 'react' | 
				 | 
			
		||||
import { FormProviderProps } from 'antd/es/form/context' | 
				 | 
			
		||||
 import type { CommonFormProps } from '@ant-design/pro-form/es/BaseForm/BaseForm' | 
				 | 
			
		||||
 | 
				 | 
			
		||||
export type CrazyFormProps<T = Record<string, any>> = { | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  onFinish?: (values: T) => Promise<boolean | void>; | 
				 | 
			
		||||
  current?: string; | 
				 | 
			
		||||
  /**、 | 
				 | 
			
		||||
   * 切换区域传透的Props | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  toggleProps?: Record<any, any>; | 
				 | 
			
		||||
  formProps?: ProFormProps<T>; | 
				 | 
			
		||||
  onCurrentChange?: (current: string) => void; | 
				 | 
			
		||||
  /** 自定义步骤器 */ | 
				 | 
			
		||||
  toggleRender?: ( | 
				 | 
			
		||||
          items: { | 
				 | 
			
		||||
            key: string; | 
				 | 
			
		||||
            title?: React.ReactNode; | 
				 | 
			
		||||
            [key: string]: any | 
				 | 
			
		||||
          }[], | 
				 | 
			
		||||
          defaultDom: React.ReactNode, | 
				 | 
			
		||||
  ) => React.ReactNode; | 
				 | 
			
		||||
  /** @name 当前展示表单的 formRef */ | 
				 | 
			
		||||
  formRef?: React.MutableRefObject<ProFormInstance<any> | undefined | null>; | 
				 | 
			
		||||
  /** @name 所有表单的 formMapRef */ | 
				 | 
			
		||||
  formMapRef?: React.MutableRefObject< | 
				 | 
			
		||||
          React.MutableRefObject<FormInstance<any> | undefined>[] | 
				 | 
			
		||||
  >; | 
				 | 
			
		||||
  /** | 
				 | 
			
		||||
   * 自定义单个表单 | 
				 | 
			
		||||
   * | 
				 | 
			
		||||
   * @param form From 的 dom,可以放置到别的位置 | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  toggleFormRender?: (from: React.ReactNode) => React.ReactNode; | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  /** | 
				 | 
			
		||||
   * 自定义整个表单区域 | 
				 | 
			
		||||
   * | 
				 | 
			
		||||
   * @param form From 的 dom,可以放置到别的位置 | 
				 | 
			
		||||
   * @param submitter 操作按钮 | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  formRender?: ( | 
				 | 
			
		||||
          from: React.ReactNode, | 
				 | 
			
		||||
          submitter: React.ReactNode, | 
				 | 
			
		||||
  ) => React.ReactNode; | 
				 | 
			
		||||
  /** 按钮的统一配置,优先级低于分步表单的配置 */ | 
				 | 
			
		||||
  submitter?: | 
				 | 
			
		||||
          | SubmitterProps<{ | 
				 | 
			
		||||
    current: string;  //当前激活的toggle
 | 
				 | 
			
		||||
    form?: FormInstance<any>; | 
				 | 
			
		||||
  }> | 
				 | 
			
		||||
          | false; | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  containerStyle?: React.CSSProperties; | 
				 | 
			
		||||
  /** | 
				 | 
			
		||||
   * 自定義整個佈局。 | 
				 | 
			
		||||
   * | 
				 | 
			
		||||
   * @param layoutDom toggleDom 和 formDom 元素可以放置在任何地方。 | 
				 | 
			
		||||
   */ | 
				 | 
			
		||||
  layoutRender?: (layoutDom: { | 
				 | 
			
		||||
    toggleDom: React.ReactElement; | 
				 | 
			
		||||
    formDom: React.ReactElement; | 
				 | 
			
		||||
  }) => React.ReactNode; | 
				 | 
			
		||||
} & Omit<FormProviderProps, 'children'>; | 
				 | 
			
		||||
 | 
				 | 
			
		||||
 | 
				 | 
			
		||||
export type CrazyChildFormProps<T = Record<string, any>, U = Record<string, any>> = { | 
				 | 
			
		||||
 | 
				 | 
			
		||||
  index?: number; | 
				 | 
			
		||||
} & Omit<FormProps<T>, 'onFinish' | 'form'> & | 
				 | 
			
		||||
        Omit<CommonFormProps<T, U>, 'submitter' | 'form'>; | 
				 | 
			
		||||
@ -0,0 +1,319 @@ | 
			
		|||||
 | 
				import { useStyle } from './style.ts' | 
			
		||||
 | 
				import { Badge, Button, Divider, Form, Popconfirm, Space, Tooltip } from 'antd' | 
			
		||||
 | 
				import { useAtom, useAtomValue, useSetAtom } from 'jotai' | 
			
		||||
 | 
				import { ModelContext, useSpanModel } from '@/store/x-form/model.ts' | 
			
		||||
 | 
				import { ReactNode, useEffect, useState } from 'react' | 
			
		||||
 | 
				import { transformAntdTableProColumns } from './utils' | 
			
		||||
 | 
				import Action from '@/components/action/Action.tsx' | 
			
		||||
 | 
				import { FilterOutlined } from '@ant-design/icons' | 
			
		||||
 | 
				import ListPageLayout from '@/layout/ListPageLayout.tsx' | 
			
		||||
 | 
				import { Table as ProTable } from '@/components/table' | 
			
		||||
 | 
				import { getValueCount, unSetColumnRules } from '@/utils' | 
			
		||||
 | 
				import { BetaSchemaForm, ProColumns, ProFormColumnsType } from '@ant-design/pro-components' | 
			
		||||
 | 
				import { useApiContext } from '@/context.ts' | 
			
		||||
 | 
				import { useDeepCompareEffect } from 'react-use' | 
			
		||||
 | 
				import { XFormTypes } from '@/types/x-form/model' | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				export interface XFormProps { | 
			
		||||
 | 
				  title?: ReactNode | 
			
		||||
 | 
				  namespace?: string | 
			
		||||
 | 
				  columns?: ProColumns[] //重写columns
 | 
			
		||||
 | 
				} | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				const XForm = ({ namespace, columns: propColumns = [], title }: XFormProps) => { | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const { styles, cx } = useStyle() | 
			
		||||
 | 
				  const apiCtx = useApiContext() | 
			
		||||
 | 
				  const { | 
			
		||||
 | 
				    apiAtom, | 
			
		||||
 | 
				    deleteModelAtom, | 
			
		||||
 | 
				    modelAtom, | 
			
		||||
 | 
				    modelCURDAtom, | 
			
		||||
 | 
				    modelsAtom, | 
			
		||||
 | 
				    modelSearchAtom, | 
			
		||||
 | 
				    saveOrUpdateModelAtom | 
			
		||||
 | 
				  } = useSpanModel(namespace || apiCtx?.menu?.meta?.name || 'default') as ModelContext | 
			
		||||
 | 
				  const [ form ] = Form.useForm() | 
			
		||||
 | 
				  const [ filterForm ] = Form.useForm() | 
			
		||||
 | 
				  const setApi = useSetAtom(apiAtom) | 
			
		||||
 | 
				  const [ model, setModel ] = useAtom<XFormTypes.IModel>(modelAtom) | 
			
		||||
 | 
				  const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateModelAtom) | 
			
		||||
 | 
				  const [ search, setSearch ] = useAtom(modelSearchAtom) | 
			
		||||
 | 
				  const { data, isFetching, isLoading, refetch } = useAtomValue(modelsAtom) | 
			
		||||
 | 
				  const { mutate: deleteModel, isPending: isDeleting } = useAtomValue(deleteModelAtom) | 
			
		||||
 | 
				  const { data: curdModal, isLoading: curdLoading, refetch: reloadCURDModal } = useAtomValue(modelCURDAtom) | 
			
		||||
 | 
				  const [ open, setOpen ] = useState(false) | 
			
		||||
 | 
				  const [ openFilter, setFilterOpen ] = useState(false) | 
			
		||||
 | 
				  const [ searchKey, setSearchKey ] = useState(search?.key) | 
			
		||||
 | 
				  const [ columns, setColumns ] = useState<ProColumns[]>([]) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  useDeepCompareEffect(() => { | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    const res = transformAntdTableProColumns(curdModal?.column || [], propColumns) | 
			
		||||
 | 
				    const _columns = [ { | 
			
		||||
 | 
				      title: 'ID', | 
			
		||||
 | 
				      dataIndex: 'id', | 
			
		||||
 | 
				      hideInTable: true, | 
			
		||||
 | 
				      hideInSearch: true, | 
			
		||||
 | 
				      formItemProps: { hidden: true } | 
			
		||||
 | 
				    } ].concat(res as any).concat([ | 
			
		||||
 | 
				      { | 
			
		||||
 | 
				        title: '操作', | 
			
		||||
 | 
				        dataIndex: 'option', | 
			
		||||
 | 
				        valueType: 'option', | 
			
		||||
 | 
				        fixed: 'right', | 
			
		||||
 | 
				        render: (_, record) => [ | 
			
		||||
 | 
				          <Action key="edit" | 
			
		||||
 | 
				                  as={'a'} | 
			
		||||
 | 
				                  onClick={() => { | 
			
		||||
 | 
				                    form.setFieldsValue(record) | 
			
		||||
 | 
				                    setOpen(true) | 
			
		||||
 | 
				                  }}>{'编辑'}</Action>, | 
			
		||||
 | 
				          <Popconfirm | 
			
		||||
 | 
				                  key={'del_confirm'} | 
			
		||||
 | 
				                  disabled={isDeleting} | 
			
		||||
 | 
				                  onConfirm={() => { | 
			
		||||
 | 
				                    deleteModel([ record.id ]) | 
			
		||||
 | 
				                  }} | 
			
		||||
 | 
				                  title={'确定要删除吗?'}> | 
			
		||||
 | 
				            <a key="del"> | 
			
		||||
 | 
				              删除 | 
			
		||||
 | 
				            </a> | 
			
		||||
 | 
				          </Popconfirm> | 
			
		||||
 | 
				        ] | 
			
		||||
 | 
				      } as any | 
			
		||||
 | 
				    ]) | 
			
		||||
 | 
				    setColumns(_columns) | 
			
		||||
 | 
				  }, [ curdModal?.column, propColumns, deleteModel, form, isDeleting, setOpen, ]) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  useEffect(() => { | 
			
		||||
 | 
				    if (apiCtx.isApi && apiCtx.api) { | 
			
		||||
 | 
				      setApi(apiCtx.api) | 
			
		||||
 | 
				      reloadCURDModal() | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				  }, [ apiCtx.isApi, apiCtx.api ]) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  useDeepCompareEffect(() => { | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    setSearchKey(search?.key) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    filterForm.setFieldsValue(search) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  }, [ search ]) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  useEffect(() => { | 
			
		||||
 | 
				    if (isSuccess) { | 
			
		||||
 | 
				      setOpen(false) | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				  }, [ isSuccess ]) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const formProps = curdModal?.dialogType === 'drawer' ? { | 
			
		||||
 | 
				    layoutType: 'DrawerForm', | 
			
		||||
 | 
				    drawerProps: { | 
			
		||||
 | 
				      maskClosable: false, | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				  } : { | 
			
		||||
 | 
				    layoutType: 'ModalForm', | 
			
		||||
 | 
				    modalProps: { | 
			
		||||
 | 
				      maskClosable: false, | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				  } | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const renderTitle = () => { | 
			
		||||
 | 
				    if (title) { | 
			
		||||
 | 
				      return title | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    if (apiCtx.menu) { | 
			
		||||
 | 
				      const { menu } = apiCtx | 
			
		||||
 | 
				      return menu.title | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				    return null | 
			
		||||
 | 
				  } | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const tableTitle = <> | 
			
		||||
 | 
				    <Button key={'add'} | 
			
		||||
 | 
				            onClick={() => { | 
			
		||||
 | 
				              form.resetFields() | 
			
		||||
 | 
				              form.setFieldsValue({ | 
			
		||||
 | 
				                id: 0, | 
			
		||||
 | 
				              }) | 
			
		||||
 | 
				              setOpen(true) | 
			
		||||
 | 
				            }} | 
			
		||||
 | 
				            type={'primary'}>{'添加'}</Button> | 
			
		||||
 | 
				  </> | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  return ( | 
			
		||||
 | 
				          <> | 
			
		||||
 | 
				            <ListPageLayout | 
			
		||||
 | 
				                    className={styles.container} | 
			
		||||
 | 
				                    title={renderTitle()}> | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				              <ProTable | 
			
		||||
 | 
				                      rowKey="id" | 
			
		||||
 | 
				                      headerTitle={tableTitle} | 
			
		||||
 | 
				                      toolbar={{ | 
			
		||||
 | 
				                        /*search: { | 
			
		||||
 | 
				                          loading: isFetching && !!search?.key, | 
			
		||||
 | 
				                          onSearch: (value: string) => { | 
			
		||||
 | 
				                            setSearch(prev => ({ | 
			
		||||
 | 
				                              ...prev, | 
			
		||||
 | 
				                              title: value | 
			
		||||
 | 
				                            })) | 
			
		||||
 | 
				                          }, | 
			
		||||
 | 
				                          allowClear: true, | 
			
		||||
 | 
				                          onChange: (e) => { | 
			
		||||
 | 
				                            setSearchKey(e.target?.value) | 
			
		||||
 | 
				                          }, | 
			
		||||
 | 
				                          value: searchKey, | 
			
		||||
 | 
				                          placeholder: '输入关键字搜索', | 
			
		||||
 | 
				                        },*/ | 
			
		||||
 | 
				                        actions: [ | 
			
		||||
 | 
				                          <Tooltip key={'filter'} title={'高级查询'}> | 
			
		||||
 | 
				                            <Badge count={getValueCount(search)}> | 
			
		||||
 | 
				                              <Button | 
			
		||||
 | 
				                                      onClick={() => { | 
			
		||||
 | 
				                                        setFilterOpen(true) | 
			
		||||
 | 
				                                      }} | 
			
		||||
 | 
				                                      icon={<FilterOutlined/>} shape={'circle'} size={'small'}/> | 
			
		||||
 | 
				                            </Badge> | 
			
		||||
 | 
				                          </Tooltip>, | 
			
		||||
 | 
				                          <Divider type={'vertical'} key={'divider'}/>, | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				                        ] | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      scroll={{ | 
			
		||||
 | 
				                        x: (columns?.length || 1) * 100, | 
			
		||||
 | 
				                        y: 'calc(100vh - 290px)' | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      search={false} | 
			
		||||
 | 
				                      onRow={(record) => { | 
			
		||||
 | 
				                        return { | 
			
		||||
 | 
				                          className: cx({ | 
			
		||||
 | 
				                            // 'ant-table-row-selected': currentMovie?.id === record.id
 | 
			
		||||
 | 
				                          }), | 
			
		||||
 | 
				                          onClick: () => { | 
			
		||||
 | 
				                            setModel(record) | 
			
		||||
 | 
				                          } | 
			
		||||
 | 
				                        } | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      dateFormatter="string" | 
			
		||||
 | 
				                      loading={isLoading || isFetching || curdLoading} | 
			
		||||
 | 
				                      dataSource={data?.rows ?? []} | 
			
		||||
 | 
				                      columns={columns} | 
			
		||||
 | 
				                      options={{ | 
			
		||||
 | 
				                        reload: () => { | 
			
		||||
 | 
				                          refetch() | 
			
		||||
 | 
				                        }, | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      pagination={{ | 
			
		||||
 | 
				                        total: data?.total, | 
			
		||||
 | 
				                        pageSize: search.pageSize, | 
			
		||||
 | 
				                        current: search.page, | 
			
		||||
 | 
				                        onShowSizeChange: (current: number, size: number) => { | 
			
		||||
 | 
				                          setSearch({ | 
			
		||||
 | 
				                            ...search, | 
			
		||||
 | 
				                            pageSize: size, | 
			
		||||
 | 
				                            page: current | 
			
		||||
 | 
				                          }) | 
			
		||||
 | 
				                        }, | 
			
		||||
 | 
				                        onChange: (current, pageSize) => { | 
			
		||||
 | 
				                          setSearch(prev => { | 
			
		||||
 | 
				                            return { | 
			
		||||
 | 
				                              ...prev, | 
			
		||||
 | 
				                              page: current, | 
			
		||||
 | 
				                              pageSize: pageSize, | 
			
		||||
 | 
				                            } | 
			
		||||
 | 
				                          }) | 
			
		||||
 | 
				                        }, | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				              /> | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				              <BetaSchemaForm | 
			
		||||
 | 
				                      grid={true} | 
			
		||||
 | 
				                      shouldUpdate={false} | 
			
		||||
 | 
				                      width={1000} | 
			
		||||
 | 
				                      form={form} | 
			
		||||
 | 
				                      layout={'vertical'} | 
			
		||||
 | 
				                      scrollToFirstError={true} | 
			
		||||
 | 
				                      title={model?.id !== 0 ? '编辑' : '添加'} | 
			
		||||
 | 
				                      {...formProps as any} | 
			
		||||
 | 
				                      open={open} | 
			
		||||
 | 
				                      onOpenChange={(open) => { | 
			
		||||
 | 
				                        setOpen(open) | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      loading={isSubmitting} | 
			
		||||
 | 
				                      onFinish={async (values) => { | 
			
		||||
 | 
				                        saveOrUpdate(values as any) | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      columns={columns as ProFormColumnsType[]}/> | 
			
		||||
 | 
				              <BetaSchemaForm | 
			
		||||
 | 
				                      title={'高级查询'} | 
			
		||||
 | 
				                      grid={true} | 
			
		||||
 | 
				                      shouldUpdate={false} | 
			
		||||
 | 
				                      width={500} | 
			
		||||
 | 
				                      form={filterForm} | 
			
		||||
 | 
				                      open={openFilter} | 
			
		||||
 | 
				                      onOpenChange={open => { | 
			
		||||
 | 
				                        setFilterOpen(open) | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      layout={'vertical'} | 
			
		||||
 | 
				                      scrollToFirstError={true} | 
			
		||||
 | 
				                      layoutType={formProps.layoutType as any} | 
			
		||||
 | 
				                      drawerProps={{ | 
			
		||||
 | 
				                        ...formProps.drawerProps, | 
			
		||||
 | 
				                        mask: false, | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      modalProps={{ | 
			
		||||
 | 
				                        ...formProps.modalProps, | 
			
		||||
 | 
				                        mask: false, | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      submitter={{ | 
			
		||||
 | 
				                        searchConfig: { | 
			
		||||
 | 
				                          resetText: '清空', | 
			
		||||
 | 
				                          submitText: '查询', | 
			
		||||
 | 
				                        }, | 
			
		||||
 | 
				                        onReset: () => { | 
			
		||||
 | 
				                          filterForm.resetFields() | 
			
		||||
 | 
				                        }, | 
			
		||||
 | 
				                        render: (props,) => { | 
			
		||||
 | 
				                          return ( | 
			
		||||
 | 
				                                  <div style={{ textAlign: 'right' }}> | 
			
		||||
 | 
				                                    <Space> | 
			
		||||
 | 
				                                      <Button onClick={() => { | 
			
		||||
 | 
				                                        props.reset() | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				                                      }}>{props.searchConfig?.resetText}</Button> | 
			
		||||
 | 
				                                      <Button type="primary" | 
			
		||||
 | 
				                                              onClick={() => { | 
			
		||||
 | 
				                                                props.submit() | 
			
		||||
 | 
				                                              }} | 
			
		||||
 | 
				                                      >{props.searchConfig?.submitText}</Button> | 
			
		||||
 | 
				                                    </Space> | 
			
		||||
 | 
				                                  </div> | 
			
		||||
 | 
				                          ) | 
			
		||||
 | 
				                        }, | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				                      onFinish={async (values) => { | 
			
		||||
 | 
				                        //处理,变成数组
 | 
			
		||||
 | 
				                        Object.keys(values).forEach(key => { | 
			
		||||
 | 
				                          if (typeof values[key] === 'string' && values[key].includes(',')) { | 
			
		||||
 | 
				                            values[key] = values[key].split(',') | 
			
		||||
 | 
				                          } | 
			
		||||
 | 
				                        }) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				                        setSearch(values) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				                      }} | 
			
		||||
 | 
				                      columns={unSetColumnRules(columns.filter(item => !item.hideInSearch) as ProFormColumnsType[])}/> | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				            </ListPageLayout> | 
			
		||||
 | 
				          </> | 
			
		||||
 | 
				  ) | 
			
		||||
 | 
				} | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				export default XForm | 
			
		||||
@ -0,0 +1,13 @@ | 
			
		|||||
 | 
				import { createStyles } from '@/theme' | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { | 
			
		||||
 | 
				    const prefix = `${prefixCls}-${token?.proPrefix}-x-form-component` | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    const container = css`
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    `
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    return { | 
			
		||||
 | 
				        container: cx(prefix, props?.className, container), | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				}) | 
			
		||||
@ -0,0 +1,155 @@ | 
			
		|||||
 | 
				import { XFormTypes } from '@/types/x-form/model' | 
			
		||||
 | 
				import { ProColumns } from '@ant-design/pro-components' | 
			
		||||
 | 
				import Switch from '@/components/switch' | 
			
		||||
 | 
				import { Checkbox, DatePicker, Input, Radio, Select, TreeSelect } from 'antd' | 
			
		||||
 | 
				import request from '@/request' | 
			
		||||
 | 
				import { convertToBool, genProTableColumnWidthProps } from '@/utils' | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				const getValueType = (column: XFormTypes.IColumn) => { | 
			
		||||
 | 
				  switch (column.type) { | 
			
		||||
 | 
				    case 'input': | 
			
		||||
 | 
				      return 'text' | 
			
		||||
 | 
				    case 'select': | 
			
		||||
 | 
				      return 'select' | 
			
		||||
 | 
				    case 'date': | 
			
		||||
 | 
				      return 'date' | 
			
		||||
 | 
				    case 'switch': | 
			
		||||
 | 
				      return 'switch' | 
			
		||||
 | 
				    case 'radio': | 
			
		||||
 | 
				      return 'radio' | 
			
		||||
 | 
				    case 'checkbox': | 
			
		||||
 | 
				      return 'checkbox' | 
			
		||||
 | 
				    case 'textarea': | 
			
		||||
 | 
				      return 'textarea' | 
			
		||||
 | 
				    case 'tree': | 
			
		||||
 | 
				      return 'treeSelect' | 
			
		||||
 | 
				    default: | 
			
		||||
 | 
				      return 'text' | 
			
		||||
 | 
				  } | 
			
		||||
 | 
				} | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				//根据type返回对应的组件
 | 
			
		||||
 | 
				const getComponent = (column: XFormTypes.IColumn) => { | 
			
		||||
 | 
				  const type = getValueType(column) as any | 
			
		||||
 | 
				  switch (type) { | 
			
		||||
 | 
				    case 'input': | 
			
		||||
 | 
				      return Input | 
			
		||||
 | 
				    case 'select': | 
			
		||||
 | 
				      return Select | 
			
		||||
 | 
				    case 'date': | 
			
		||||
 | 
				      return DatePicker | 
			
		||||
 | 
				    case 'switch': | 
			
		||||
 | 
				      return Switch | 
			
		||||
 | 
				    case 'radio': | 
			
		||||
 | 
				      return Radio | 
			
		||||
 | 
				    case 'checkbox': | 
			
		||||
 | 
				      return Checkbox | 
			
		||||
 | 
				    case 'textarea': | 
			
		||||
 | 
				      return Input.TextArea | 
			
		||||
 | 
				    case 'tree': | 
			
		||||
 | 
				    case 'treeSelect': | 
			
		||||
 | 
				      return TreeSelect | 
			
		||||
 | 
				    default: | 
			
		||||
 | 
				      return Input | 
			
		||||
 | 
				  } | 
			
		||||
 | 
				} | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				export const transformAntdTableProColumns = (columns: XFormTypes.IColumn[], overwriteColumns?: ProColumns[]) => { | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  const overwriteKeys = [] as string[] | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				  return (columns || []).map(item => { | 
			
		||||
 | 
				    const { value, props, multiple, checkStrictly } = item | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    const { width, fieldProps: _fieldProps } = genProTableColumnWidthProps(item.width) | 
			
		||||
 | 
				    const fieldProps: ProColumns['fieldProps'] = { | 
			
		||||
 | 
				      dataFiledNames: props, | 
			
		||||
 | 
				      ...(multiple ? { multiple: true } : {}), | 
			
		||||
 | 
				      ...(checkStrictly ? { treeCheckStrictly: true } : {}), | 
			
		||||
 | 
				      ..._fieldProps, | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
 | 
				    const formItemProps: ProColumns['formItemProps'] = (_form, _config) => { | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				      return { | 
			
		||||
 | 
				        rules: item.rules?.map(i => { | 
			
		||||
 | 
				          return { | 
			
		||||
 | 
				            required: i.required, | 
			
		||||
 | 
				            message: i.message | 
			
		||||
 | 
				          } | 
			
		||||
 | 
				        }), | 
			
		||||
 | 
				        ...(value ? { valuePropName: value } : {}) | 
			
		||||
 | 
				      } | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    const rowProps = item.gutter ? { gutter: item.gutter } : { gutter: [ 16, 0 ], } | 
			
		||||
 | 
				    const colProps = item.span ? { span: item.span } : {} | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    const type = getValueType(item) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    const overwrite = overwriteColumns?.find(i => i.dataIndex === item.prop) | 
			
		||||
 | 
				    if (overwrite) { | 
			
		||||
 | 
				      overwriteKeys.push(item.prop) | 
			
		||||
 | 
				    } | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				    return { | 
			
		||||
 | 
				      title: item.label, | 
			
		||||
 | 
				      dataIndex: item.prop, | 
			
		||||
 | 
				      key: item.prop, | 
			
		||||
 | 
				      width, | 
			
		||||
 | 
				      valueType: type, | 
			
		||||
 | 
				      hideInSearch: !item.search, | 
			
		||||
 | 
				      hideInTable: item.hide, | 
			
		||||
 | 
				      fieldProps, | 
			
		||||
 | 
				      formItemProps, | 
			
		||||
 | 
				      colProps, | 
			
		||||
 | 
				      rowProps, | 
			
		||||
 | 
				      request: item.dicUrl ? async (params, props) => { | 
			
		||||
 | 
				        const { fieldProps: { dataFiledNames } } = props | 
			
		||||
 | 
				        const { value, res: resKey, label } = dataFiledNames || {} | 
			
		||||
 | 
				        const url = `/${item.dicUrl.replace(/^:/, '/')}` | 
			
		||||
 | 
				        return request[item.dicMethod || 'get'](url, params).then(res => { | 
			
		||||
 | 
				          return (res.data?.[resKey] || res.data || []).map((i: any) => { | 
			
		||||
 | 
				            // console.log(i)
 | 
			
		||||
 | 
				            const disabled = 'disabled' in i ? i.disabled : | 
			
		||||
 | 
				                    ('status' in i ? !convertToBool(i.status) : false) | 
			
		||||
 | 
				            return { | 
			
		||||
 | 
				              title: i[label || 'label'], | 
			
		||||
 | 
				              label: i[label || 'label'], | 
			
		||||
 | 
				              value: i[value || 'id'], | 
			
		||||
 | 
				              disabled, | 
			
		||||
 | 
				              data: i | 
			
		||||
 | 
				            } | 
			
		||||
 | 
				          }) | 
			
		||||
 | 
				        }) | 
			
		||||
 | 
				      } : undefined, | 
			
		||||
 | 
				      renderFormItem: (_scheam, config) => { | 
			
		||||
 | 
				        const Component = getComponent(item) as any | 
			
		||||
 | 
				        const { options, ...props } = config as any | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				        if ([ 'tree', 'treeSelect' ].includes(_scheam.valueType as string)) { | 
			
		||||
 | 
				          return <Component {...props} treeData={options}/> | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				        if (_scheam.valueType as string === 'select') { | 
			
		||||
 | 
				          return <Select {...props} options={options}/> | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				        return <Component {...config} /> | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      render: (text: any, record: any) => { | 
			
		||||
 | 
				        if (type === 'switch' || type === 'checkbox' || type === 'radio') { | 
			
		||||
 | 
				          return <Switch size={'small'} value={record[item.prop]}/> | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				        if (item.colorFormat) { | 
			
		||||
 | 
				          return <span style={{ color: item.colorFormat }}>{text}</span> | 
			
		||||
 | 
				        } | 
			
		||||
 | 
				        return text | 
			
		||||
 | 
				      }, | 
			
		||||
 | 
				      ...overwrite | 
			
		||||
 | 
				    } as ProColumns | 
			
		||||
 | 
				  }).concat(overwriteColumns?.filter(i => !overwriteKeys.includes(i.dataIndex)) || []) | 
			
		||||
 | 
				
 | 
			
		||||
 | 
				} | 
			
		||||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue