#
tokens: 47490/50000 7/1152 files (page 28/52)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 28 of 52. Use http://codebase.md/alibaba/formily?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .all-contributorsrc
├── .codecov.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows
│       ├── check-pr-title.yml
│       ├── ci.yml
│       ├── commitlint.yml
│       ├── issue-open-check.yml
│       ├── package-size.yml
│       └── pr-welcome.yml
├── .gitignore
├── .prettierrc.js
├── .umirc.js
├── .vscode
│   └── cspell.json
├── .yarnrc
├── CHANGELOG.md
├── commitlint.config.js
├── devtools
│   ├── .eslintrc
│   └── chrome-extension
│       ├── .npmignore
│       ├── assets
│       │   └── img
│       │       ├── loading.svg
│       │       └── logo
│       │           ├── 128x128.png
│       │           ├── 16x16.png
│       │           ├── 38x38.png
│       │           ├── 48x48.png
│       │           ├── error.png
│       │           ├── gray.png
│       │           └── scalable.png
│       ├── config
│       │   ├── webpack.base.ts
│       │   ├── webpack.dev.ts
│       │   └── webpack.prod.ts
│       ├── LICENSE.md
│       ├── package.json
│       ├── src
│       │   ├── app
│       │   │   ├── components
│       │   │   │   ├── FieldTree.tsx
│       │   │   │   ├── filter.ts
│       │   │   │   ├── LeftPanel.tsx
│       │   │   │   ├── RightPanel.tsx
│       │   │   │   ├── SearchBox.tsx
│       │   │   │   └── Tabs.tsx
│       │   │   ├── demo.tsx
│       │   │   └── index.tsx
│       │   └── extension
│       │       ├── backend.ts
│       │       ├── background.ts
│       │       ├── content.ts
│       │       ├── devpanel.tsx
│       │       ├── devtools.tsx
│       │       ├── inject.ts
│       │       ├── manifest.json
│       │       ├── popup.tsx
│       │       └── views
│       │           ├── devpanel.ejs
│       │           ├── devtools.ejs
│       │           └── popup.ejs
│       ├── tsconfig.build.json
│       └── tsconfig.json
├── docs
│   ├── functions
│   │   ├── contributors.ts
│   │   └── npm-search.ts
│   ├── guide
│   │   ├── advanced
│   │   │   ├── async.md
│   │   │   ├── async.zh-CN.md
│   │   │   ├── build.md
│   │   │   ├── build.zh-CN.md
│   │   │   ├── business-logic.md
│   │   │   ├── business-logic.zh-CN.md
│   │   │   ├── calculator.md
│   │   │   ├── calculator.zh-CN.md
│   │   │   ├── controlled.md
│   │   │   ├── controlled.zh-CN.md
│   │   │   ├── custom.md
│   │   │   ├── custom.zh-CN.md
│   │   │   ├── destructor.md
│   │   │   ├── destructor.zh-CN.md
│   │   │   ├── input.less
│   │   │   ├── layout.md
│   │   │   ├── layout.zh-CN.md
│   │   │   ├── linkages.md
│   │   │   ├── linkages.zh-CN.md
│   │   │   ├── validate.md
│   │   │   └── validate.zh-CN.md
│   │   ├── contribution.md
│   │   ├── contribution.zh-CN.md
│   │   ├── form-builder.md
│   │   ├── form-builder.zh-CN.md
│   │   ├── index.md
│   │   ├── index.zh-CN.md
│   │   ├── issue-helper.md
│   │   ├── issue-helper.zh-CN.md
│   │   ├── learn-formily.md
│   │   ├── learn-formily.zh-CN.md
│   │   ├── quick-start.md
│   │   ├── quick-start.zh-CN.md
│   │   ├── scenes
│   │   │   ├── dialog-drawer.md
│   │   │   ├── dialog-drawer.zh-CN.md
│   │   │   ├── edit-detail.md
│   │   │   ├── edit-detail.zh-CN.md
│   │   │   ├── index.less
│   │   │   ├── login-register.md
│   │   │   ├── login-register.zh-CN.md
│   │   │   ├── more.md
│   │   │   ├── more.zh-CN.md
│   │   │   ├── query-list.md
│   │   │   ├── query-list.zh-CN.md
│   │   │   ├── step-form.md
│   │   │   ├── step-form.zh-CN.md
│   │   │   ├── tab-form.md
│   │   │   ├── tab-form.zh-CN.md
│   │   │   └── VerifyCode.tsx
│   │   ├── upgrade.md
│   │   └── upgrade.zh-CN.md
│   ├── index.md
│   ├── index.zh-CN.md
│   └── site
│       ├── Contributors.less
│       ├── Contributors.tsx
│       ├── QrCode.less
│       ├── QrCode.tsx
│       ├── Section.less
│       ├── Section.tsx
│       └── styles.less
├── global.config.ts
├── jest.config.js
├── lerna.json
├── LICENSE.md
├── package.json
├── packages
│   ├── .eslintrc
│   ├── antd
│   │   ├── __tests__
│   │   │   ├── moment.spec.ts
│   │   │   └── sideEffects.spec.ts
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── build-style.ts
│   │   ├── create-style.ts
│   │   ├── docs
│   │   │   ├── components
│   │   │   │   ├── ArrayCards.md
│   │   │   │   ├── ArrayCards.zh-CN.md
│   │   │   │   ├── ArrayCollapse.md
│   │   │   │   ├── ArrayCollapse.zh-CN.md
│   │   │   │   ├── ArrayItems.md
│   │   │   │   ├── ArrayItems.zh-CN.md
│   │   │   │   ├── ArrayTable.md
│   │   │   │   ├── ArrayTable.zh-CN.md
│   │   │   │   ├── ArrayTabs.md
│   │   │   │   ├── ArrayTabs.zh-CN.md
│   │   │   │   ├── Cascader.md
│   │   │   │   ├── Cascader.zh-CN.md
│   │   │   │   ├── Checkbox.md
│   │   │   │   ├── Checkbox.zh-CN.md
│   │   │   │   ├── DatePicker.md
│   │   │   │   ├── DatePicker.zh-CN.md
│   │   │   │   ├── Editable.md
│   │   │   │   ├── Editable.zh-CN.md
│   │   │   │   ├── Form.md
│   │   │   │   ├── Form.zh-CN.md
│   │   │   │   ├── FormButtonGroup.md
│   │   │   │   ├── FormButtonGroup.zh-CN.md
│   │   │   │   ├── FormCollapse.md
│   │   │   │   ├── FormCollapse.zh-CN.md
│   │   │   │   ├── FormDialog.md
│   │   │   │   ├── FormDialog.zh-CN.md
│   │   │   │   ├── FormDrawer.md
│   │   │   │   ├── FormDrawer.zh-CN.md
│   │   │   │   ├── FormGrid.md
│   │   │   │   ├── FormGrid.zh-CN.md
│   │   │   │   ├── FormItem.md
│   │   │   │   ├── FormItem.zh-CN.md
│   │   │   │   ├── FormLayout.md
│   │   │   │   ├── FormLayout.zh-CN.md
│   │   │   │   ├── FormStep.md
│   │   │   │   ├── FormStep.zh-CN.md
│   │   │   │   ├── FormTab.md
│   │   │   │   ├── FormTab.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   ├── index.zh-CN.md
│   │   │   │   ├── Input.md
│   │   │   │   ├── Input.zh-CN.md
│   │   │   │   ├── NumberPicker.md
│   │   │   │   ├── NumberPicker.zh-CN.md
│   │   │   │   ├── Password.md
│   │   │   │   ├── Password.zh-CN.md
│   │   │   │   ├── PreviewText.md
│   │   │   │   ├── PreviewText.zh-CN.md
│   │   │   │   ├── Radio.md
│   │   │   │   ├── Radio.zh-CN.md
│   │   │   │   ├── Reset.md
│   │   │   │   ├── Reset.zh-CN.md
│   │   │   │   ├── Select.md
│   │   │   │   ├── Select.zh-CN.md
│   │   │   │   ├── SelectTable.md
│   │   │   │   ├── SelectTable.zh-CN.md
│   │   │   │   ├── Space.md
│   │   │   │   ├── Space.zh-CN.md
│   │   │   │   ├── Submit.md
│   │   │   │   ├── Submit.zh-CN.md
│   │   │   │   ├── Switch.md
│   │   │   │   ├── Switch.zh-CN.md
│   │   │   │   ├── TimePicker.md
│   │   │   │   ├── TimePicker.zh-CN.md
│   │   │   │   ├── Transfer.md
│   │   │   │   ├── Transfer.zh-CN.md
│   │   │   │   ├── TreeSelect.md
│   │   │   │   ├── TreeSelect.zh-CN.md
│   │   │   │   ├── Upload.md
│   │   │   │   └── Upload.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __builtins__
│   │   │   │   ├── hooks
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── useClickAway.ts
│   │   │   │   │   └── usePrefixCls.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── loading.ts
│   │   │   │   ├── moment.ts
│   │   │   │   ├── pickDataProps.ts
│   │   │   │   ├── portal.tsx
│   │   │   │   ├── render.ts
│   │   │   │   └── sort.tsx
│   │   │   ├── array-base
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-cards
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-collapse
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-items
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-table
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-tabs
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── cascader
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── checkbox
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── date-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── editable
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form-button-group
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form-collapse
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-dialog
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-drawer
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-grid
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form-item
│   │   │   │   ├── animation.less
│   │   │   │   ├── grid.less
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form-layout
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   ├── style.ts
│   │   │   │   └── useResponsiveFormLayout.ts
│   │   │   ├── form-step
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-tab
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── index.ts
│   │   │   ├── input
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── number-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── password
│   │   │   │   ├── index.tsx
│   │   │   │   ├── PasswordStrength.tsx
│   │   │   │   └── style.ts
│   │   │   ├── preview-text
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── radio
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── reset
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── select
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── select-table
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   ├── style.ts
│   │   │   │   ├── useCheckSlackly.tsx
│   │   │   │   ├── useFilterOptions.tsx
│   │   │   │   ├── useFlatOptions.tsx
│   │   │   │   ├── useSize.tsx
│   │   │   │   ├── useTitleAddon.tsx
│   │   │   │   └── utils.ts
│   │   │   ├── space
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── style.less
│   │   │   ├── style.ts
│   │   │   ├── submit
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── switch
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── time-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── transfer
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── tree-select
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   └── upload
│   │   │       ├── index.tsx
│   │   │       ├── placeholder.ts
│   │   │       └── style.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── benchmark
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   └── index.tsx
│   │   ├── template.ejs
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.json
│   │   ├── webpack.base.ts
│   │   ├── webpack.dev.ts
│   │   └── webpack.prod.ts
│   ├── core
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── docs
│   │   │   ├── api
│   │   │   │   ├── entry
│   │   │   │   │   ├── ActionResponse.less
│   │   │   │   │   ├── ActionResponse.tsx
│   │   │   │   │   ├── createForm.md
│   │   │   │   │   ├── createForm.zh-CN.md
│   │   │   │   │   ├── FieldEffectHooks.md
│   │   │   │   │   ├── FieldEffectHooks.zh-CN.md
│   │   │   │   │   ├── FormChecker.md
│   │   │   │   │   ├── FormChecker.zh-CN.md
│   │   │   │   │   ├── FormEffectHooks.md
│   │   │   │   │   ├── FormEffectHooks.zh-CN.md
│   │   │   │   │   ├── FormHooksAPI.md
│   │   │   │   │   ├── FormHooksAPI.zh-CN.md
│   │   │   │   │   ├── FormPath.md
│   │   │   │   │   ├── FormPath.zh-CN.md
│   │   │   │   │   ├── FormValidatorRegistry.md
│   │   │   │   │   └── FormValidatorRegistry.zh-CN.md
│   │   │   │   └── models
│   │   │   │       ├── ArrayField.md
│   │   │   │       ├── ArrayField.zh-CN.md
│   │   │   │       ├── Field.md
│   │   │   │       ├── Field.zh-CN.md
│   │   │   │       ├── Form.md
│   │   │   │       ├── Form.zh-CN.md
│   │   │   │       ├── ObjectField.md
│   │   │   │       ├── ObjectField.zh-CN.md
│   │   │   │       ├── Query.md
│   │   │   │       ├── Query.zh-CN.md
│   │   │   │       ├── VoidField.md
│   │   │   │       └── VoidField.zh-CN.md
│   │   │   ├── guide
│   │   │   │   ├── architecture.md
│   │   │   │   ├── architecture.zh-CN.md
│   │   │   │   ├── field.md
│   │   │   │   ├── field.zh-CN.md
│   │   │   │   ├── form.md
│   │   │   │   ├── form.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   ├── index.zh-CN.md
│   │   │   │   ├── mvvm.md
│   │   │   │   ├── mvvm.zh-CN.md
│   │   │   │   ├── values.md
│   │   │   │   └── values.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── array.spec.ts
│   │   │   │   ├── effects.spec.ts
│   │   │   │   ├── externals.spec.ts
│   │   │   │   ├── field.spec.ts
│   │   │   │   ├── form.spec.ts
│   │   │   │   ├── graph.spec.ts
│   │   │   │   ├── heart.spec.ts
│   │   │   │   ├── internals.spec.ts
│   │   │   │   ├── lifecycle.spec.ts
│   │   │   │   ├── object.spec.ts
│   │   │   │   ├── shared.ts
│   │   │   │   └── void.spec.ts
│   │   │   ├── effects
│   │   │   │   ├── index.ts
│   │   │   │   ├── onFieldEffects.ts
│   │   │   │   └── onFormEffects.ts
│   │   │   ├── global.d.ts
│   │   │   ├── index.ts
│   │   │   ├── models
│   │   │   │   ├── ArrayField.ts
│   │   │   │   ├── BaseField.ts
│   │   │   │   ├── Field.ts
│   │   │   │   ├── Form.ts
│   │   │   │   ├── Graph.ts
│   │   │   │   ├── Heart.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── LifeCycle.ts
│   │   │   │   ├── ObjectField.ts
│   │   │   │   ├── Query.ts
│   │   │   │   ├── types.ts
│   │   │   │   └── VoidField.ts
│   │   │   ├── shared
│   │   │   │   ├── checkers.ts
│   │   │   │   ├── constants.ts
│   │   │   │   ├── effective.ts
│   │   │   │   ├── externals.ts
│   │   │   │   └── internals.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── element
│   │   ├── .npmignore
│   │   ├── build-style.ts
│   │   ├── create-style.ts
│   │   ├── docs
│   │   │   ├── .vuepress
│   │   │   │   ├── components
│   │   │   │   │   ├── createCodeSandBox.js
│   │   │   │   │   ├── dumi-previewer.vue
│   │   │   │   │   └── highlight.js
│   │   │   │   ├── config.js
│   │   │   │   ├── enhanceApp.js
│   │   │   │   ├── styles
│   │   │   │   │   └── index.styl
│   │   │   │   └── util.js
│   │   │   ├── demos
│   │   │   │   ├── guide
│   │   │   │   │   ├── array-cards
│   │   │   │   │   │   ├── effects-json-schema.vue
│   │   │   │   │   │   ├── effects-markup-schema.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── array-collapse
│   │   │   │   │   │   ├── effects-json-schema.vue
│   │   │   │   │   │   ├── effects-markup-schema.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── array-items
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── array-table
│   │   │   │   │   │   ├── effects-json-schema.vue
│   │   │   │   │   │   ├── effects-markup-schema.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── array-tabs
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── cascader
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── checkbox
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── date-picker
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── editable
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-button-group.vue
│   │   │   │   │   ├── form-collapse
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── form-dialog
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-drawer
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-grid
│   │   │   │   │   │   ├── form.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── native.vue
│   │   │   │   │   ├── form-item
│   │   │   │   │   │   ├── bordered-none.vue
│   │   │   │   │   │   ├── common.vue
│   │   │   │   │   │   ├── feedback.vue
│   │   │   │   │   │   ├── inset.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   ├── size.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-layout
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-step
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── form-tab
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── form.vue
│   │   │   │   │   ├── input
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── input-number
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── password
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── preview-text
│   │   │   │   │   │   ├── base.vue
│   │   │   │   │   │   └── extend.vue
│   │   │   │   │   ├── radio
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── reset
│   │   │   │   │   │   ├── base.vue
│   │   │   │   │   │   ├── force.vue
│   │   │   │   │   │   └── validate.vue
│   │   │   │   │   ├── select
│   │   │   │   │   │   ├── json-schema-async.vue
│   │   │   │   │   │   ├── json-schema-sync.vue
│   │   │   │   │   │   ├── markup-schema-async-search.vue
│   │   │   │   │   │   ├── markup-schema-async.vue
│   │   │   │   │   │   ├── markup-schema-sync.vue
│   │   │   │   │   │   ├── template-async.vue
│   │   │   │   │   │   └── template-sync.vue
│   │   │   │   │   ├── space
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── submit
│   │   │   │   │   │   ├── base.vue
│   │   │   │   │   │   └── loading.vue
│   │   │   │   │   ├── switch
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── time-picker
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── transfer
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   └── upload
│   │   │   │   │       ├── json-schema.vue
│   │   │   │   │       ├── markup-schema.vue
│   │   │   │   │       └── template.vue
│   │   │   │   └── index.vue
│   │   │   ├── guide
│   │   │   │   ├── array-cards.md
│   │   │   │   ├── array-collapse.md
│   │   │   │   ├── array-items.md
│   │   │   │   ├── array-table.md
│   │   │   │   ├── array-tabs.md
│   │   │   │   ├── cascader.md
│   │   │   │   ├── checkbox.md
│   │   │   │   ├── date-picker.md
│   │   │   │   ├── editable.md
│   │   │   │   ├── form-button-group.md
│   │   │   │   ├── form-collapse.md
│   │   │   │   ├── form-dialog.md
│   │   │   │   ├── form-drawer.md
│   │   │   │   ├── form-grid.md
│   │   │   │   ├── form-item.md
│   │   │   │   ├── form-layout.md
│   │   │   │   ├── form-step.md
│   │   │   │   ├── form-tab.md
│   │   │   │   ├── form.md
│   │   │   │   ├── index.md
│   │   │   │   ├── input-number.md
│   │   │   │   ├── input.md
│   │   │   │   ├── password.md
│   │   │   │   ├── preview-text.md
│   │   │   │   ├── radio.md
│   │   │   │   ├── reset.md
│   │   │   │   ├── select.md
│   │   │   │   ├── space.md
│   │   │   │   ├── submit.md
│   │   │   │   ├── switch.md
│   │   │   │   ├── time-picker.md
│   │   │   │   ├── transfer.md
│   │   │   │   └── upload.md
│   │   │   └── README.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __builtins__
│   │   │   │   ├── configs
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── shared
│   │   │   │   │   ├── create-context.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── loading.ts
│   │   │   │   │   ├── portal.ts
│   │   │   │   │   ├── resolve-component.ts
│   │   │   │   │   ├── transform-component.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   └── styles
│   │   │   │       └── common.scss
│   │   │   ├── array-base
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-cards
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-collapse
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-items
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-table
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-tabs
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── cascader
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── checkbox
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── date-picker
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── editable
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── el-form
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── el-form-item
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── form
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-button-group
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-collapse
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-dialog
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── form-drawer
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-grid
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-item
│   │   │   │   ├── animation.scss
│   │   │   │   ├── grid.scss
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   ├── style.ts
│   │   │   │   └── var.scss
│   │   │   ├── form-layout
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   ├── style.ts
│   │   │   │   └── useResponsiveFormLayout.ts
│   │   │   ├── form-step
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── form-tab
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── index.ts
│   │   │   ├── input
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── input-number
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── password
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── preview-text
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── radio
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── reset
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── select
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── space
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── style.ts
│   │   │   ├── submit
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── switch
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── time-picker
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── transfer
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   └── upload
│   │   │       ├── index.ts
│   │   │       └── style.ts
│   │   ├── transformer.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── grid
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── index.ts
│   │   │   └── observer.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── json-schema
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── __snapshots__
│   │   │   │   │   └── schema.spec.ts.snap
│   │   │   │   ├── compiler.spec.ts
│   │   │   │   ├── patches.spec.ts
│   │   │   │   ├── schema.spec.ts
│   │   │   │   ├── server-validate.spec.ts
│   │   │   │   ├── shared.spec.ts
│   │   │   │   ├── transformer.spec.ts
│   │   │   │   └── traverse.spec.ts
│   │   │   ├── compiler.ts
│   │   │   ├── global.d.ts
│   │   │   ├── index.ts
│   │   │   ├── patches.ts
│   │   │   ├── polyfills
│   │   │   │   ├── index.ts
│   │   │   │   └── SPECIFICATION_1_0.ts
│   │   │   ├── schema.ts
│   │   │   ├── shared.ts
│   │   │   ├── transformer.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── next
│   │   ├── __tests__
│   │   │   ├── moment.spec.ts
│   │   │   └── sideEffects.spec.ts
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── build-style.ts
│   │   ├── create-style.ts
│   │   ├── docs
│   │   │   ├── components
│   │   │   │   ├── ArrayCards.md
│   │   │   │   ├── ArrayCards.zh-CN.md
│   │   │   │   ├── ArrayCollapse.md
│   │   │   │   ├── ArrayCollapse.zh-CN.md
│   │   │   │   ├── ArrayItems.md
│   │   │   │   ├── ArrayItems.zh-CN.md
│   │   │   │   ├── ArrayTable.md
│   │   │   │   ├── ArrayTable.zh-CN.md
│   │   │   │   ├── Cascader.md
│   │   │   │   ├── Cascader.zh-CN.md
│   │   │   │   ├── Checkbox.md
│   │   │   │   ├── Checkbox.zh-CN.md
│   │   │   │   ├── DatePicker.md
│   │   │   │   ├── DatePicker.zh-CN.md
│   │   │   │   ├── DatePicker2.md
│   │   │   │   ├── DatePicker2.zh-CN.md
│   │   │   │   ├── Editable.md
│   │   │   │   ├── Editable.zh-CN.md
│   │   │   │   ├── Form.md
│   │   │   │   ├── Form.zh-CN.md
│   │   │   │   ├── FormButtonGroup.md
│   │   │   │   ├── FormButtonGroup.zh-CN.md
│   │   │   │   ├── FormCollapse.md
│   │   │   │   ├── FormCollapse.zh-CN.md
│   │   │   │   ├── FormDialog.md
│   │   │   │   ├── FormDialog.zh-CN.md
│   │   │   │   ├── FormDrawer.md
│   │   │   │   ├── FormDrawer.zh-CN.md
│   │   │   │   ├── FormGrid.md
│   │   │   │   ├── FormGrid.zh-CN.md
│   │   │   │   ├── FormItem.md
│   │   │   │   ├── FormItem.zh-CN.md
│   │   │   │   ├── FormLayout.md
│   │   │   │   ├── FormLayout.zh-CN.md
│   │   │   │   ├── FormStep.md
│   │   │   │   ├── FormStep.zh-CN.md
│   │   │   │   ├── FormTab.md
│   │   │   │   ├── FormTab.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   ├── index.zh-CN.md
│   │   │   │   ├── Input.md
│   │   │   │   ├── Input.zh-CN.md
│   │   │   │   ├── NumberPicker.md
│   │   │   │   ├── NumberPicker.zh-CN.md
│   │   │   │   ├── Password.md
│   │   │   │   ├── Password.zh-CN.md
│   │   │   │   ├── PreviewText.md
│   │   │   │   ├── PreviewText.zh-CN.md
│   │   │   │   ├── Radio.md
│   │   │   │   ├── Radio.zh-CN.md
│   │   │   │   ├── Reset.md
│   │   │   │   ├── Reset.zh-CN.md
│   │   │   │   ├── Select.md
│   │   │   │   ├── Select.zh-CN.md
│   │   │   │   ├── SelectTable.md
│   │   │   │   ├── SelectTable.zh-CN.md
│   │   │   │   ├── Space.md
│   │   │   │   ├── Space.zh-CN.md
│   │   │   │   ├── Submit.md
│   │   │   │   ├── Submit.zh-CN.md
│   │   │   │   ├── Switch.md
│   │   │   │   ├── Switch.zh-CN.md
│   │   │   │   ├── TimePicker.md
│   │   │   │   ├── TimePicker.zh-CN.md
│   │   │   │   ├── TimePicker2.md
│   │   │   │   ├── TimePicker2.zh-CN.md
│   │   │   │   ├── Transfer.md
│   │   │   │   ├── Transfer.zh-CN.md
│   │   │   │   ├── TreeSelect.md
│   │   │   │   ├── TreeSelect.zh-CN.md
│   │   │   │   ├── Upload.md
│   │   │   │   └── Upload.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LESENCE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __builtins__
│   │   │   │   ├── empty.tsx
│   │   │   │   ├── hooks
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── useClickAway.ts
│   │   │   │   │   └── usePrefixCls.ts
│   │   │   │   ├── icons.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── loading.ts
│   │   │   │   ├── mapSize.ts
│   │   │   │   ├── mapStatus.ts
│   │   │   │   ├── moment.ts
│   │   │   │   ├── pickDataProps.ts
│   │   │   │   ├── portal.tsx
│   │   │   │   ├── render.ts
│   │   │   │   └── toArray.ts
│   │   │   ├── array-base
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-cards
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-collapse
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-items
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-table
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── cascader
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── checkbox
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── date-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── date-picker2
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── editable
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── form
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-button-group
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-collapse
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-dialog
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-drawer
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-grid
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-item
│   │   │   │   ├── animation.scss
│   │   │   │   ├── grid.scss
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   ├── scss
│   │   │   │   │   └── variable.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-layout
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   ├── style.ts
│   │   │   │   └── useResponsiveFormLayout.ts
│   │   │   ├── form-step
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-tab
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── index.ts
│   │   │   ├── input
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── main.scss
│   │   │   ├── number-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── password
│   │   │   │   ├── index.tsx
│   │   │   │   ├── PasswordStrength.tsx
│   │   │   │   └── style.ts
│   │   │   ├── preview-text
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── radio
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── reset
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── select
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── select-table
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   ├── style.ts
│   │   │   │   ├── useCheckSlackly.tsx
│   │   │   │   ├── useFilterOptions.tsx
│   │   │   │   ├── useFlatOptions.tsx
│   │   │   │   ├── useSize.tsx
│   │   │   │   ├── useTitleAddon.tsx
│   │   │   │   └── utils.ts
│   │   │   ├── space
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── style.ts
│   │   │   ├── submit
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── switch
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── time-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── time-picker2
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── transfer
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── tree-select
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   └── upload
│   │   │       ├── index.tsx
│   │   │       ├── main.scss
│   │   │       ├── placeholder.ts
│   │   │       └── style.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── path
│   │   ├── .npmignore
│   │   ├── benchmark.ts
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── accessor.spec.ts
│   │   │   │   ├── basic.spec.ts
│   │   │   │   ├── match.spec.ts
│   │   │   │   ├── parser.spec.ts
│   │   │   │   └── share.spec.ts
│   │   │   ├── contexts.ts
│   │   │   ├── destructor.ts
│   │   │   ├── index.ts
│   │   │   ├── matcher.ts
│   │   │   ├── parser.ts
│   │   │   ├── shared.ts
│   │   │   ├── tokenizer.ts
│   │   │   ├── tokens.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── react
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── docs
│   │   │   ├── api
│   │   │   │   ├── components
│   │   │   │   │   ├── ArrayField.md
│   │   │   │   │   ├── ArrayField.zh-CN.md
│   │   │   │   │   ├── ExpressionScope.md
│   │   │   │   │   ├── ExpressionScope.zh-CN.md
│   │   │   │   │   ├── Field.md
│   │   │   │   │   ├── Field.zh-CN.md
│   │   │   │   │   ├── FormConsumer.md
│   │   │   │   │   ├── FormConsumer.zh-CN.md
│   │   │   │   │   ├── FormProvider.md
│   │   │   │   │   ├── FormProvider.zh-CN.md
│   │   │   │   │   ├── ObjectField.md
│   │   │   │   │   ├── ObjectField.zh-CN.md
│   │   │   │   │   ├── RecordScope.md
│   │   │   │   │   ├── RecordScope.zh-CN.md
│   │   │   │   │   ├── RecordsScope.md
│   │   │   │   │   ├── RecordsScope.zh-CN.md
│   │   │   │   │   ├── RecursionField.md
│   │   │   │   │   ├── RecursionField.zh-CN.md
│   │   │   │   │   ├── SchemaField.md
│   │   │   │   │   ├── SchemaField.zh-CN.md
│   │   │   │   │   ├── VoidField.md
│   │   │   │   │   └── VoidField.zh-CN.md
│   │   │   │   ├── hooks
│   │   │   │   │   ├── useExpressionScope.md
│   │   │   │   │   ├── useExpressionScope.zh-CN.md
│   │   │   │   │   ├── useField.md
│   │   │   │   │   ├── useField.zh-CN.md
│   │   │   │   │   ├── useFieldSchema.md
│   │   │   │   │   ├── useFieldSchema.zh-CN.md
│   │   │   │   │   ├── useForm.md
│   │   │   │   │   ├── useForm.zh-CN.md
│   │   │   │   │   ├── useFormEffects.md
│   │   │   │   │   ├── useFormEffects.zh-CN.md
│   │   │   │   │   ├── useParentForm.md
│   │   │   │   │   └── useParentForm.zh-CN.md
│   │   │   │   └── shared
│   │   │   │       ├── connect.md
│   │   │   │       ├── connect.zh-CN.md
│   │   │   │       ├── context.md
│   │   │   │       ├── context.zh-CN.md
│   │   │   │       ├── mapProps.md
│   │   │   │       ├── mapProps.zh-CN.md
│   │   │   │       ├── mapReadPretty.md
│   │   │   │       ├── mapReadPretty.zh-CN.md
│   │   │   │       ├── observer.md
│   │   │   │       ├── observer.zh-CN.md
│   │   │   │       ├── Schema.md
│   │   │   │       └── Schema.zh-CN.md
│   │   │   ├── guide
│   │   │   │   ├── architecture.md
│   │   │   │   ├── architecture.zh-CN.md
│   │   │   │   ├── concept.md
│   │   │   │   ├── concept.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   └── index.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── expression.spec.tsx
│   │   │   │   ├── field.spec.tsx
│   │   │   │   ├── form.spec.tsx
│   │   │   │   ├── schema.json.spec.tsx
│   │   │   │   ├── schema.markup.spec.tsx
│   │   │   │   └── shared.tsx
│   │   │   ├── components
│   │   │   │   ├── ArrayField.tsx
│   │   │   │   ├── ExpressionScope.tsx
│   │   │   │   ├── Field.tsx
│   │   │   │   ├── FormConsumer.tsx
│   │   │   │   ├── FormProvider.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── ObjectField.tsx
│   │   │   │   ├── ReactiveField.tsx
│   │   │   │   ├── RecordScope.tsx
│   │   │   │   ├── RecordsScope.tsx
│   │   │   │   ├── RecursionField.tsx
│   │   │   │   ├── SchemaField.tsx
│   │   │   │   └── VoidField.tsx
│   │   │   ├── global.d.ts
│   │   │   ├── hooks
│   │   │   │   ├── index.ts
│   │   │   │   ├── useAttach.ts
│   │   │   │   ├── useExpressionScope.ts
│   │   │   │   ├── useField.ts
│   │   │   │   ├── useFieldSchema.ts
│   │   │   │   ├── useForm.ts
│   │   │   │   ├── useFormEffects.ts
│   │   │   │   └── useParentForm.ts
│   │   │   ├── index.ts
│   │   │   ├── shared
│   │   │   │   ├── connect.ts
│   │   │   │   ├── context.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── render.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── reactive
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── benchmark.ts
│   │   ├── docs
│   │   │   ├── api
│   │   │   │   ├── action.md
│   │   │   │   ├── action.zh-CN.md
│   │   │   │   ├── autorun.md
│   │   │   │   ├── autorun.zh-CN.md
│   │   │   │   ├── batch.md
│   │   │   │   ├── batch.zh-CN.md
│   │   │   │   ├── define.md
│   │   │   │   ├── define.zh-CN.md
│   │   │   │   ├── hasCollected.md
│   │   │   │   ├── hasCollected.zh-CN.md
│   │   │   │   ├── markObservable.md
│   │   │   │   ├── markObservable.zh-CN.md
│   │   │   │   ├── markRaw.md
│   │   │   │   ├── markRaw.zh-CN.md
│   │   │   │   ├── model.md
│   │   │   │   ├── model.zh-CN.md
│   │   │   │   ├── observable.md
│   │   │   │   ├── observable.zh-CN.md
│   │   │   │   ├── observe.md
│   │   │   │   ├── observe.zh-CN.md
│   │   │   │   ├── raw.md
│   │   │   │   ├── raw.zh-CN.md
│   │   │   │   ├── react
│   │   │   │   │   ├── observer.md
│   │   │   │   │   └── observer.zh-CN.md
│   │   │   │   ├── reaction.md
│   │   │   │   ├── reaction.zh-CN.md
│   │   │   │   ├── toJS.md
│   │   │   │   ├── toJS.zh-CN.md
│   │   │   │   ├── tracker.md
│   │   │   │   ├── tracker.zh-CN.md
│   │   │   │   ├── typeChecker.md
│   │   │   │   ├── typeChecker.zh-CN.md
│   │   │   │   ├── untracked.md
│   │   │   │   ├── untracked.zh-CN.md
│   │   │   │   └── vue
│   │   │   │       ├── observer.md
│   │   │   │       └── observer.zh-CN.md
│   │   │   ├── guide
│   │   │   │   ├── best-practice.md
│   │   │   │   ├── best-practice.zh-CN.md
│   │   │   │   ├── concept.md
│   │   │   │   ├── concept.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   └── index.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── action.spec.ts
│   │   │   │   ├── annotations.spec.ts
│   │   │   │   ├── array.spec.ts
│   │   │   │   ├── autorun.spec.ts
│   │   │   │   ├── batch.spec.ts
│   │   │   │   ├── collections-map.spec.ts
│   │   │   │   ├── collections-set.spec.ts
│   │   │   │   ├── collections-weakmap.spec.ts
│   │   │   │   ├── collections-weakset.spec.ts
│   │   │   │   ├── define.spec.ts
│   │   │   │   ├── externals.spec.ts
│   │   │   │   ├── hasCollected.spec.ts
│   │   │   │   ├── observable.spec.ts
│   │   │   │   ├── observe.spec.ts
│   │   │   │   ├── tracker.spec.ts
│   │   │   │   └── untracked.spec.ts
│   │   │   ├── action.ts
│   │   │   ├── annotations
│   │   │   │   ├── box.ts
│   │   │   │   ├── computed.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── observable.ts
│   │   │   │   ├── ref.ts
│   │   │   │   └── shallow.ts
│   │   │   ├── array.ts
│   │   │   ├── autorun.ts
│   │   │   ├── batch.ts
│   │   │   ├── checkers.ts
│   │   │   ├── environment.ts
│   │   │   ├── externals.ts
│   │   │   ├── global.d.ts
│   │   │   ├── handlers.ts
│   │   │   ├── index.ts
│   │   │   ├── internals.ts
│   │   │   ├── model.ts
│   │   │   ├── observable.ts
│   │   │   ├── observe.ts
│   │   │   ├── reaction.ts
│   │   │   ├── tracker.ts
│   │   │   ├── tree.ts
│   │   │   ├── types.ts
│   │   │   └── untracked.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── reactive-react
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── hooks
│   │   │   │   ├── index.ts
│   │   │   │   ├── useCompatEffect.ts
│   │   │   │   ├── useCompatFactory.ts
│   │   │   │   ├── useDidUpdate.ts
│   │   │   │   ├── useForceUpdate.ts
│   │   │   │   ├── useLayoutEffect.ts
│   │   │   │   └── useObserver.ts
│   │   │   ├── index.ts
│   │   │   ├── observer.ts
│   │   │   ├── shared
│   │   │   │   ├── gc.ts
│   │   │   │   ├── global.ts
│   │   │   │   ├── immediate.ts
│   │   │   │   └── index.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── reactive-test-cases-for-react18
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── index.js
│   │   │   └── MySlowList.js
│   │   ├── template.ejs
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.json
│   │   ├── webpack.base.ts
│   │   ├── webpack.dev.ts
│   │   └── webpack.prod.ts
│   ├── reactive-vue
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   └── observer.spec.ts
│   │   │   ├── hooks
│   │   │   │   ├── index.ts
│   │   │   │   └── useObserver.ts
│   │   │   ├── index.ts
│   │   │   ├── observer
│   │   │   │   ├── collectData.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── observerInVue2.ts
│   │   │   │   └── observerInVue3.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── shared
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   └── index.spec.ts
│   │   │   ├── array.ts
│   │   │   ├── case.ts
│   │   │   ├── checkers.ts
│   │   │   ├── clone.ts
│   │   │   ├── compare.ts
│   │   │   ├── defaults.ts
│   │   │   ├── deprecate.ts
│   │   │   ├── global.ts
│   │   │   ├── index.ts
│   │   │   ├── instanceof.ts
│   │   │   ├── isEmpty.ts
│   │   │   ├── merge.ts
│   │   │   ├── middleware.ts
│   │   │   ├── path.ts
│   │   │   ├── string.ts
│   │   │   ├── subscribable.ts
│   │   │   └── uid.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── validator
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── parser.spec.ts
│   │   │   │   ├── registry.spec.ts
│   │   │   │   └── validator.spec.ts
│   │   │   ├── formats.ts
│   │   │   ├── index.ts
│   │   │   ├── locale.ts
│   │   │   ├── parser.ts
│   │   │   ├── registry.ts
│   │   │   ├── rules.ts
│   │   │   ├── template.ts
│   │   │   ├── types.ts
│   │   │   └── validator.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   └── vue
│       ├── .npmignore
│       ├── bin
│       │   ├── formily-vue-fix.js
│       │   └── formily-vue-switch.js
│       ├── docs
│       │   ├── .vuepress
│       │   │   ├── components
│       │   │   │   ├── createCodeSandBox.js
│       │   │   │   ├── dumi-previewer.vue
│       │   │   │   └── highlight.js
│       │   │   ├── config.js
│       │   │   ├── enhanceApp.js
│       │   │   └── styles
│       │   │       └── index.styl
│       │   ├── api
│       │   │   ├── components
│       │   │   │   ├── array-field.md
│       │   │   │   ├── expression-scope.md
│       │   │   │   ├── field.md
│       │   │   │   ├── form-consumer.md
│       │   │   │   ├── form-provider.md
│       │   │   │   ├── object-field.md
│       │   │   │   ├── recursion-field-with-component.md
│       │   │   │   ├── recursion-field.md
│       │   │   │   ├── schema-field-with-schema.md
│       │   │   │   ├── schema-field.md
│       │   │   │   └── void-field.md
│       │   │   ├── hooks
│       │   │   │   ├── use-field-schema.md
│       │   │   │   ├── use-field.md
│       │   │   │   ├── use-form-effects.md
│       │   │   │   ├── use-form.md
│       │   │   │   └── use-parent-form.md
│       │   │   └── shared
│       │   │       ├── connect.md
│       │   │       ├── injections.md
│       │   │       ├── map-props.md
│       │   │       ├── map-read-pretty.md
│       │   │       ├── observer.md
│       │   │       └── schema.md
│       │   ├── demos
│       │   │   ├── api
│       │   │   │   ├── components
│       │   │   │   │   ├── array-field.vue
│       │   │   │   │   ├── expression-scope.vue
│       │   │   │   │   ├── field.vue
│       │   │   │   │   ├── form-consumer.vue
│       │   │   │   │   ├── form-provider.vue
│       │   │   │   │   ├── object-field.vue
│       │   │   │   │   ├── recursion-field-with-component.vue
│       │   │   │   │   ├── recursion-field.vue
│       │   │   │   │   ├── schema-field-with-schema.vue
│       │   │   │   │   ├── schema-field.vue
│       │   │   │   │   └── void-field.vue
│       │   │   │   ├── hooks
│       │   │   │   │   ├── use-field-schema.vue
│       │   │   │   │   ├── use-field.vue
│       │   │   │   │   ├── use-form-effects.vue
│       │   │   │   │   ├── use-form.vue
│       │   │   │   │   └── use-parent-form.vue
│       │   │   │   └── shared
│       │   │   │       ├── connect.vue
│       │   │   │       ├── map-props.vue
│       │   │   │       ├── map-read-pretty.vue
│       │   │   │       └── observer.vue
│       │   │   ├── index.vue
│       │   │   └── questions
│       │   │       ├── default-slot.vue
│       │   │       ├── events.vue
│       │   │       ├── named-slot.vue
│       │   │       └── scoped-slot.vue
│       │   ├── guide
│       │   │   ├── architecture.md
│       │   │   ├── concept.md
│       │   │   └── README.md
│       │   ├── questions
│       │   │   └── README.md
│       │   └── README.md
│       ├── package.json
│       ├── README.md
│       ├── rollup.config.js
│       ├── scripts
│       │   ├── postinstall.js
│       │   ├── switch-cli.js
│       │   └── utils.js
│       ├── src
│       │   ├── __tests__
│       │   │   ├── expression.scope.spec.ts
│       │   │   ├── field.spec.ts
│       │   │   ├── form.spec.ts
│       │   │   ├── schema.json.spec.ts
│       │   │   ├── schema.markup.spec.ts
│       │   │   ├── shared.spec.ts
│       │   │   └── utils.spec.ts
│       │   ├── components
│       │   │   ├── ArrayField.ts
│       │   │   ├── ExpressionScope.ts
│       │   │   ├── Field.ts
│       │   │   ├── FormConsumer.ts
│       │   │   ├── FormProvider.ts
│       │   │   ├── index.ts
│       │   │   ├── ObjectField.ts
│       │   │   ├── ReactiveField.ts
│       │   │   ├── RecursionField.ts
│       │   │   ├── SchemaField.ts
│       │   │   └── VoidField.ts
│       │   ├── global.d.ts
│       │   ├── hooks
│       │   │   ├── index.ts
│       │   │   ├── useAttach.ts
│       │   │   ├── useField.ts
│       │   │   ├── useFieldSchema.ts
│       │   │   ├── useForm.ts
│       │   │   ├── useFormEffects.ts
│       │   │   ├── useInjectionCleaner.ts
│       │   │   └── useParentForm.ts
│       │   ├── index.ts
│       │   ├── shared
│       │   │   ├── connect.ts
│       │   │   ├── context.ts
│       │   │   ├── createForm.ts
│       │   │   ├── fragment.ts
│       │   │   ├── h.ts
│       │   │   └── index.ts
│       │   ├── types
│       │   │   └── index.ts
│       │   ├── utils
│       │   │   ├── formatVNodeData.ts
│       │   │   ├── getFieldProps.ts
│       │   │   ├── getRawComponent.ts
│       │   │   └── resolveSchemaProps.ts
│       │   └── vue2-components.ts
│       ├── tsconfig.build.json
│       ├── tsconfig.json
│       └── tsconfig.types.json
├── README.md
├── README.zh-cn.md
├── scripts
│   ├── build-style
│   │   ├── buildAllStyles.ts
│   │   ├── copy.ts
│   │   ├── helper.ts
│   │   └── index.ts
│   └── rollup.base.js
├── tsconfig.build.json
├── tsconfig.jest.json
├── tsconfig.json
└── yarn.lock
```

# Files

--------------------------------------------------------------------------------
/packages/element/src/form-dialog/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { createForm, Form, IFormProps } from '@formily/core'
  2 | import { toJS } from '@formily/reactive'
  3 | import { observer } from '@formily/reactive-vue'
  4 | import {
  5 |   applyMiddleware,
  6 |   IMiddleware,
  7 |   isBool,
  8 |   isFn,
  9 |   isNum,
 10 |   isStr,
 11 | } from '@formily/shared'
 12 | import { FormProvider, Fragment, h } from '@formily/vue'
 13 | import type { Button as ButtonProps, Dialog as DialogProps } from 'element-ui'
 14 | import { Button, Dialog } from 'element-ui'
 15 | import { t } from 'element-ui/src/locale'
 16 | import { Portal, PortalTarget } from 'portal-vue'
 17 | import Vue, { Component, VNode } from 'vue'
 18 | import { defineComponent } from 'vue-demi'
 19 | import { stylePrefix } from '../__builtins__/configs'
 20 | import {
 21 |   createPortalProvider,
 22 |   getProtalContext,
 23 |   isValidElement,
 24 |   loading,
 25 |   resolveComponent,
 26 | } from '../__builtins__/shared'
 27 | 
 28 | type FormDialogContentProps = { form: Form }
 29 | 
 30 | type FormDialogContent = Component | ((props: FormDialogContentProps) => VNode)
 31 | 
 32 | type DialogTitle = string | number | Component | VNode | (() => VNode)
 33 | 
 34 | type IFormDialogProps = Omit<DialogProps, 'title'> & {
 35 |   title?: DialogTitle
 36 |   footer?: null | Component | VNode | (() => VNode)
 37 |   cancelText?: string | Component | VNode | (() => VNode)
 38 |   cancelButtonProps?: ButtonProps
 39 |   okText?: string | Component | VNode | (() => VNode)
 40 |   okButtonProps?: ButtonProps
 41 |   onOpen?: () => void
 42 |   onOpened?: () => void
 43 |   onClose?: () => void
 44 |   onClosed?: () => void
 45 |   onCancel?: () => void
 46 |   onOK?: () => void
 47 |   loadingText?: string
 48 | }
 49 | 
 50 | const PORTAL_TARGET_NAME = 'FormDialogFooter'
 51 | 
 52 | const isDialogTitle = (props: any): props is DialogTitle => {
 53 |   return isNum(props) || isStr(props) || isBool(props) || isValidElement(props)
 54 | }
 55 | 
 56 | const getDialogProps = (props: any): IFormDialogProps => {
 57 |   if (isDialogTitle(props)) {
 58 |     return {
 59 |       title: props,
 60 |     } as IFormDialogProps
 61 |   } else {
 62 |     return props
 63 |   }
 64 | }
 65 | 
 66 | export interface IFormDialog {
 67 |   forOpen(middleware: IMiddleware<IFormProps>): IFormDialog
 68 |   forConfirm(middleware: IMiddleware<Form>): IFormDialog
 69 |   forCancel(middleware: IMiddleware<Form>): IFormDialog
 70 |   open(props?: IFormProps): Promise<any>
 71 |   close(): void
 72 | }
 73 | 
 74 | export interface IFormDialogComponentProps {
 75 |   content: FormDialogContent
 76 |   resolve: () => any
 77 |   reject: () => any
 78 | }
 79 | 
 80 | export function FormDialog(
 81 |   title: IFormDialogProps | DialogTitle,
 82 |   content: FormDialogContent
 83 | ): IFormDialog
 84 | 
 85 | export function FormDialog(
 86 |   title: IFormDialogProps | DialogTitle,
 87 |   id: string | symbol,
 88 |   content: FormDialogContent
 89 | ): IFormDialog
 90 | 
 91 | export function FormDialog(
 92 |   title: DialogTitle,
 93 |   id: string,
 94 |   content: FormDialogContent
 95 | ): IFormDialog
 96 | 
 97 | export function FormDialog(
 98 |   title: IFormDialogProps | DialogTitle,
 99 |   id: string | symbol | FormDialogContent,
100 |   content?: FormDialogContent
101 | ): IFormDialog {
102 |   if (isFn(id) || isValidElement(id)) {
103 |     content = id as FormDialogContent
104 |     id = 'form-dialog'
105 |   }
106 | 
107 |   const prefixCls = `${stylePrefix}-form-dialog`
108 |   const env = {
109 |     root: document.createElement('div'),
110 |     form: null,
111 |     promise: null,
112 |     instance: null,
113 |     openMiddlewares: [],
114 |     confirmMiddlewares: [],
115 |     cancelMiddlewares: [],
116 |   }
117 | 
118 |   document.body.appendChild(env.root)
119 | 
120 |   const props = getDialogProps(title)
121 |   const dialogProps = {
122 |     ...props,
123 |     onClosed: () => {
124 |       props.onClosed?.()
125 |       env.instance.$destroy()
126 |       env.instance = null
127 |       env.root?.parentNode?.removeChild(env.root)
128 |       env.root = undefined
129 |     },
130 |   }
131 | 
132 |   const component = observer(
133 |     defineComponent({
134 |       setup() {
135 |         return () =>
136 |           h(
137 |             Fragment,
138 |             {},
139 |             {
140 |               default: () =>
141 |                 resolveComponent(content, {
142 |                   form: env.form,
143 |                 }),
144 |             }
145 |           )
146 |       },
147 |     })
148 |   )
149 | 
150 |   const render = (visible = true, resolve?: () => any, reject?: () => any) => {
151 |     if (!env.instance) {
152 |       const ComponentConstructor = observer(
153 |         Vue.extend({
154 |           props: ['dialogProps'],
155 |           data() {
156 |             return {
157 |               visible: false,
158 |             }
159 |           },
160 |           render() {
161 |             const {
162 |               onClose,
163 |               onClosed,
164 |               onOpen,
165 |               onOpened,
166 |               onOK,
167 |               onCancel,
168 |               title,
169 |               footer,
170 |               okText,
171 |               cancelText,
172 |               okButtonProps,
173 |               cancelButtonProps,
174 |               ...dialogProps
175 |             } = this.dialogProps
176 | 
177 |             return h(
178 |               FormProvider,
179 |               {
180 |                 props: {
181 |                   form: env.form,
182 |                 },
183 |               },
184 |               {
185 |                 default: () =>
186 |                   h(
187 |                     Dialog,
188 |                     {
189 |                       class: [`${prefixCls}`],
190 |                       attrs: {
191 |                         visible: this.visible,
192 |                         ...dialogProps,
193 |                       },
194 |                       on: {
195 |                         'update:visible': (val) => {
196 |                           this.visible = val
197 |                         },
198 |                         close: () => {
199 |                           onClose?.()
200 |                         },
201 | 
202 |                         closed: () => {
203 |                           onClosed?.()
204 |                         },
205 |                         open: () => {
206 |                           onOpen?.()
207 |                         },
208 |                         opened: () => {
209 |                           onOpened?.()
210 |                         },
211 |                       },
212 |                     },
213 |                     {
214 |                       default: () => [h(component, {}, {})],
215 |                       title: () =>
216 |                         h(
217 |                           'div',
218 |                           {},
219 |                           { default: () => resolveComponent(title) }
220 |                         ),
221 |                       footer: () =>
222 |                         h(
223 |                           'div',
224 |                           {},
225 |                           {
226 |                             default: () => {
227 |                               const FooterProtalTarget = h(
228 |                                 PortalTarget,
229 |                                 {
230 |                                   props: {
231 |                                     name: PORTAL_TARGET_NAME,
232 |                                     slim: true,
233 |                                   },
234 |                                 },
235 |                                 {}
236 |                               )
237 |                               if (footer === null) {
238 |                                 return [null, FooterProtalTarget]
239 |                               } else if (footer) {
240 |                                 return [
241 |                                   resolveComponent(footer),
242 |                                   FooterProtalTarget,
243 |                                 ]
244 |                               }
245 | 
246 |                               return [
247 |                                 h(
248 |                                   Button,
249 |                                   {
250 |                                     attrs: {
251 |                                       ...cancelButtonProps
252 |                                     },
253 |                                     on: {
254 |                                       click: (e) => {
255 |                                         onCancel?.(e)
256 |                                         reject()
257 |                                       },
258 |                                     },
259 |                                   },
260 |                                   {
261 |                                     default: () =>
262 |                                       resolveComponent(
263 |                                         cancelText ||
264 |                                           t('el.popconfirm.cancelButtonText')
265 |                                       ),
266 |                                   }
267 |                                 ),
268 | 
269 |                                 h(
270 |                                   Button,
271 |                                   {
272 |                                     attrs: {
273 |                                       type: 'primary',
274 |                                       ...okButtonProps,
275 |                                       loading: env.form.submitting,
276 |                                     },
277 |                                     on: {
278 |                                       click: (e) => {
279 |                                         onOK?.(e)
280 |                                         resolve()
281 |                                       },
282 |                                     },
283 |                                   },
284 |                                   {
285 |                                     default: () =>
286 |                                       resolveComponent(
287 |                                         okText ||
288 |                                           t('el.popconfirm.confirmButtonText')
289 |                                       ),
290 |                                   }
291 |                                 ),
292 |                                 FooterProtalTarget,
293 |                               ]
294 |                             },
295 |                           }
296 |                         ),
297 |                     }
298 |                   ),
299 |               }
300 |             )
301 |           },
302 |         })
303 |       )
304 |       env.instance = new ComponentConstructor({
305 |         propsData: {
306 |           dialogProps,
307 |         },
308 |         parent: getProtalContext(id as string | symbol),
309 |       })
310 |       env.instance.$mount(env.root)
311 |       env.root = env.instance.$el
312 |     }
313 | 
314 |     env.instance.visible = visible
315 |   }
316 | 
317 |   const formDialog = {
318 |     forOpen: (middleware: IMiddleware<IFormProps>) => {
319 |       if (isFn(middleware)) {
320 |         env.openMiddlewares.push(middleware)
321 |       }
322 |       return formDialog
323 |     },
324 |     forConfirm: (middleware: IMiddleware<Form>) => {
325 |       if (isFn(middleware)) {
326 |         env.confirmMiddlewares.push(middleware)
327 |       }
328 |       return formDialog
329 |     },
330 |     forCancel: (middleware: IMiddleware<Form>) => {
331 |       if (isFn(middleware)) {
332 |         env.cancelMiddlewares.push(middleware)
333 |       }
334 |       return formDialog
335 |     },
336 |     open: (props: IFormProps) => {
337 |       if (env.promise) return env.promise
338 | 
339 |       env.promise = new Promise(async (resolve, reject) => {
340 |         try {
341 |           props = await loading(dialogProps.loadingText, () =>
342 |             applyMiddleware(props, env.openMiddlewares)
343 |           )
344 |           env.form = env.form || createForm(props)
345 |         } catch (e) {
346 |           reject(e)
347 |         }
348 | 
349 |         render(
350 |           true,
351 |           () => {
352 |             env.form
353 |               .submit(async () => {
354 |                 await applyMiddleware(env.form, env.confirmMiddlewares)
355 |                 resolve(toJS(env.form.values))
356 |                 if (dialogProps.beforeClose) {
357 |                   setTimeout(() => {
358 |                     dialogProps.beforeClose(() => {
359 |                       formDialog.close()
360 |                     })
361 |                   })
362 |                 } else {
363 |                   formDialog.close()
364 |                 }
365 |               })
366 |               .catch(() => {})
367 |           },
368 |           async () => {
369 |             await loading(dialogProps.loadingText, () =>
370 |               applyMiddleware(env.form, env.cancelMiddlewares)
371 |             )
372 | 
373 |             if (dialogProps.beforeClose) {
374 |               dialogProps.beforeClose(() => {
375 |                 formDialog.close()
376 |               })
377 |             } else {
378 |               formDialog.close()
379 |             }
380 |           }
381 |         )
382 |       })
383 |       return env.promise
384 |     },
385 |     close: () => {
386 |       if (!env.root) return
387 |       render(false)
388 |     },
389 |   }
390 |   return formDialog
391 | }
392 | 
393 | const FormDialogFooter = defineComponent({
394 |   name: 'FFormDialogFooter',
395 |   setup(props, { slots }) {
396 |     return () => {
397 |       return h(
398 |         Portal,
399 |         {
400 |           props: {
401 |             to: PORTAL_TARGET_NAME,
402 |           },
403 |         },
404 |         slots
405 |       )
406 |     }
407 |   },
408 | })
409 | 
410 | FormDialog.Footer = FormDialogFooter
411 | FormDialog.Portal = createPortalProvider('form-dialog')
412 | 
413 | export default FormDialog
414 | 
```

--------------------------------------------------------------------------------
/docs/guide/issue-helper.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Issue Helper
  2 | 
  3 | ## Before You Start...
  4 | 
  5 | The issue list is reserved exclusively for bug reports and feature requests. That means we do not accept usage questions. If you open an issue that does not conform to the requirements, it will be closed immediately.
  6 | 
  7 | For usage questions, please use the following resources:
  8 | 
  9 | - Read the introduce and components documentation
 10 | - Make sure you have search your question in FAQ and changelog
 11 | - Look for / ask questions on [Discussions](https://github.com/alibaba/formily/discussions)
 12 | 
 13 | Also try to search for your issue
 14 | 
 15 | it may have already been answered or even fixed in the development branch. However, if you find that an old, closed issue still persists in the latest version, you should open a new issue using the form below instead of commenting on the old issue.
 16 | 
 17 | ```tsx
 18 | import React from 'react'
 19 | import { createForm, onFieldMount, onFieldReact } from '@formily/core'
 20 | import { Field, VoidField } from '@formily/react'
 21 | import {
 22 |   Form,
 23 |   Input,
 24 |   Select,
 25 |   Radio,
 26 |   FormItem,
 27 |   FormButtonGroup,
 28 |   Submit,
 29 | } from '@formily/antd'
 30 | import semver from 'semver'
 31 | import ReactMde from 'react-mde'
 32 | import * as Showdown from 'showdown'
 33 | import 'react-mde/lib/styles/css/react-mde-all.css'
 34 | 
 35 | const converter = new Showdown.Converter({
 36 |   tables: true,
 37 |   simplifiedAutoLink: true,
 38 |   strikethrough: true,
 39 |   tasklists: true,
 40 | })
 41 | 
 42 | const MdInput = ({ value, onChange }) => {
 43 |   const [selectedTab, setSelectedTab] = React.useState('write')
 44 |   return (
 45 |     <div style={{ fontSize: 12, lineHeight: 1 }}>
 46 |       <ReactMde
 47 |         value={value}
 48 |         onChange={onChange}
 49 |         selectedTab={selectedTab}
 50 |         onTabChange={setSelectedTab}
 51 |         generateMarkdownPreview={(markdown) =>
 52 |           Promise.resolve(
 53 |             `<div class="markdown" style="margin:0 20px;">${
 54 |               converter.makeHtml(markdown) || ''
 55 |             }</div>`
 56 |           )
 57 |         }
 58 |       />
 59 |     </div>
 60 |   )
 61 | }
 62 | 
 63 | const form = createForm({
 64 |   validateFirst: true,
 65 |   effects() {
 66 |     onFieldMount('version', async (field) => {
 67 |       const { versions: unsort } = await fetch(
 68 |         'https://registry.npmmirror.com/@formily/core'
 69 |       ).then((res) => res.json())
 70 | 
 71 |       const versions = Object.keys(unsort).sort((v1, v2) =>
 72 |         semver.gte(v1, v2) ? -1 : 1
 73 |       )
 74 |       field.dataSource = versions.map((version) => ({
 75 |         label: version,
 76 |         value: version,
 77 |       }))
 78 |     })
 79 |     onFieldMount('package', async (field) => {
 80 |       const packages = await fetch(
 81 |         'https://formilyjs.org/.netlify/functions/npm-search?q=@formily'
 82 |       ).then((res) => res.json())
 83 |       field.dataSource = packages.map(({ name }) => {
 84 |         return {
 85 |           label: name,
 86 |           value: name,
 87 |         }
 88 |       })
 89 |     })
 90 |     onFieldReact('bug-desc', (field) => {
 91 |       field.visible = field.query('type').value() === 'Bug Report'
 92 |     })
 93 |     onFieldReact('feature-desc', (field) => {
 94 |       field.visible = field.query('type').value() === 'Feature Request'
 95 |     })
 96 |   },
 97 | })
 98 | 
 99 | const createIssueURL = ({
100 |   type,
101 |   title,
102 |   version,
103 |   package: pkg,
104 |   reproduceLink,
105 |   reproduceStep,
106 |   expected,
107 |   actually,
108 |   comment,
109 |   feature,
110 |   api,
111 | }) => {
112 |   const url = new URL('https://github.com/alibaba/formily/issues/new')
113 | 
114 |   const bugInfo = `
115 | - [ ] I have searched the [issues](https://github.com/alibaba/formily/issues) of this repository and believe that this is not a duplicate.
116 | 
117 | ### Reproduction link
118 | [![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](${
119 |     reproduceLink || ''
120 |   })
121 | 
122 | ### Steps to reproduce
123 | ${reproduceStep || ''}
124 | 
125 | ### What is expected?
126 | ${expected || ''}
127 | 
128 | ### What is actually happening?
129 | ${actually || ''}
130 | 
131 | ### Package
132 | ${pkg}@${version}
133 | 
134 | ---
135 | 
136 | ${comment || ''}
137 | 
138 | <!-- generated by formily-issue-helper. DO NOT REMOVE -->
139 | `
140 | 
141 |   const prInfo = `
142 | - [ ] I have searched the [issues](https://github.com/alibaba/formily/issues) of this repository and believe that this is not a duplicate.
143 | 
144 | ### What problem does this feature solve?
145 | ${feature || ''}
146 | 
147 | ### What does the proposed API look like?
148 | ${api || ''}
149 | 
150 | 
151 | <!-- generated by formily-issue-helper. DO NOT REMOVE -->
152 | `
153 | 
154 |   url.searchParams.set('title', `[${type}] ${title}`)
155 |   url.searchParams.set('body', type === 'Bug Report' ? bugInfo : prInfo)
156 | 
157 |   return url.href
158 | }
159 | 
160 | export default () => {
161 |   return (
162 |     <Form form={form} layout="vertical" size="large">
163 |       <Field
164 |         title="This is a"
165 |         name="type"
166 |         required
167 |         initialValue="Bug Report"
168 |         decorator={[FormItem]}
169 |         component={[Radio.Group, { optionType: 'button' }]}
170 |         dataSource={[
171 |           { label: 'Bug Report', value: 'Bug Report' },
172 |           { label: 'Feature Request', value: 'Feature Request' },
173 |         ]}
174 |       />
175 |       <Field
176 |         title="Title"
177 |         name="title"
178 |         required
179 |         decorator={[FormItem]}
180 |         component={[Input]}
181 |       />
182 |       <VoidField name="bug-desc">
183 |         <Field
184 |           title="Package"
185 |           name="package"
186 |           required
187 |           decorator={[FormItem]}
188 |           component={[Select, { showSearch: true }]}
189 |         />
190 |         <Field
191 |           title="Version"
192 |           description="Check if the issue is reproducible with the latest stable version."
193 |           name="version"
194 |           required
195 |           decorator={[FormItem]}
196 |           component={[Select, { showSearch: true }]}
197 |         />
198 | 
199 |         <Field
200 |           title="Link to minimal reproduction"
201 |           name="reproduceLink"
202 |           decorator={[FormItem]}
203 |           component={[Input]}
204 |           required
205 |           validator={[
206 |             'url',
207 |             (value) => {
208 |               return /\/\/(codesandbox\.io|github)/.test(value)
209 |                 ? ''
210 |                 : 'Must Be Codesandbox Link or Github Repo'
211 |             },
212 |           ]}
213 |           description={
214 |             <div>
215 |               This is Codesandbox templates.If you are:
216 |               <ul>
217 |                 <li>
218 |                   React + Antd User:
219 |                   <ul>
220 |                     <li>
221 |                       <a
222 |                         href="https://codesandbox.io/s/formily-react-antd-pure-jsx-omncis"
223 |                         target="_blank"
224 |                         rel="noreferrer"
225 |                       >
226 |                         Pure JSX
227 |                       </a>
228 |                     </li>
229 |                     <li>
230 |                       <a
231 |                         href="https://codesandbox.io/s/formily-react-antd-markup-schema-fvpevx"
232 |                         target="_blank"
233 |                         rel="noreferrer"
234 |                       >
235 |                         Markup Schema
236 |                       </a>
237 |                     </li>
238 |                     <li>
239 |                       <a
240 |                         href="https://codesandbox.io/s/formily-react-antd-json-schema-28p0fh"
241 |                         target="_blank"
242 |                         rel="noreferrer"
243 |                       >
244 |                         JSON Schema
245 |                       </a>
246 |                     </li>
247 |                   </ul>
248 |                 </li>
249 |                 <li>
250 |                   React + Fusion User:
251 |                   <ul>
252 |                     <li>
253 |                       <a
254 |                         href="https://codesandbox.io/s/formily-react-next-pure-jsx-ji9iiu"
255 |                         target="_blank"
256 |                         rel="noreferrer"
257 |                       >
258 |                         Pure JSX
259 |                       </a>
260 |                     </li>
261 |                     <li>
262 |                       <a
263 |                         href="https://codesandbox.io/s/formily-react-next-markup-schema-i7dm17"
264 |                         target="_blank"
265 |                         rel="noreferrer"
266 |                       >
267 |                         Markup Schema
268 |                       </a>
269 |                     </li>
270 |                     <li>
271 |                       <a
272 |                         href="https://codesandbox.io/s/formily-react-next-json-schema-1lm35h"
273 |                         target="_blank"
274 |                         rel="noreferrer"
275 |                       >
276 |                         JSON Schema
277 |                       </a>
278 |                     </li>
279 |                   </ul>
280 |                 </li>
281 |                 <li>
282 |                   Vue3 + ant-design-vue User:
283 |                   <ul>
284 |                     <li>
285 |                       <a
286 |                         href="https://codesandbox.io/s/formily-antd-vue-pure-jsx-pp3gvv"
287 |                         target="_blank"
288 |                         rel="noreferrer"
289 |                       >
290 |                         Pure JSX
291 |                       </a>
292 |                     </li>
293 |                     <li>
294 |                       <a
295 |                         href="https://codesandbox.io/s/formily-vue-ant-design-vue-markup-schema-donivp"
296 |                         target="_blank"
297 |                         rel="noreferrer"
298 |                       >
299 |                         Markup Schema
300 |                       </a>
301 |                     </li>
302 |                     <li>
303 |                       <a
304 |                         href="https://codesandbox.io/s/formily-vue-ant-design-vue-json-schema-25g4z1"
305 |                         target="_blank"
306 |                         rel="noreferrer"
307 |                       >
308 |                         JSON Schema
309 |                       </a>
310 |                     </li>
311 |                   </ul>
312 |                 </li>
313 |               </ul>
314 |             </div>
315 |           }
316 |         />
317 |         <Field
318 |           title="Step to reproduce"
319 |           description="Clear and concise reproduction instructions are important for us to be able to triage your issue in a timely manner. Note that you can use Markdown to format lists and code."
320 |           name="reproduceStep"
321 |           decorator={[FormItem]}
322 |           component={[MdInput]}
323 |           required
324 |         />
325 |         <Field
326 |           title="What is expected?"
327 |           name="expected"
328 |           decorator={[FormItem]}
329 |           component={[MdInput]}
330 |           required
331 |         />
332 |         <Field
333 |           title="What is actually happening?"
334 |           name="actually"
335 |           decorator={[FormItem]}
336 |           component={[MdInput]}
337 |           required
338 |         />
339 |         <Field
340 |           title="Any additional comments? (optional)"
341 |           name="comment"
342 |           decorator={[FormItem]}
343 |           component={[MdInput]}
344 |         />
345 |       </VoidField>
346 |       <VoidField name="feature-desc">
347 |         <Field
348 |           title="What problem does this feature solve?"
349 |           description={
350 |             <div>
351 |               <p>
352 |                 Explain your use case, context, and rationale behind this
353 |                 feature request. More importantly, what is the end user
354 |                 experience you are trying to build that led to the need for this
355 |                 feature?
356 |               </p>
357 |               <p>
358 |                 An important design goal of Formily is keeping the API surface
359 |                 small and straightforward. In general, we only consider adding
360 |                 new features that solve a problem that cannot be easily dealt
361 |                 with using existing APIs (i.e. not just an alternative way of
362 |                 doing things that can already be done). The problem should also
363 |                 be common enough to justify the addition.
364 |               </p>
365 |             </div>
366 |           }
367 |           name="feature"
368 |           required
369 |           decorator={[FormItem]}
370 |           component={[MdInput]}
371 |         />
372 | 
373 |         <Field
374 |           title="What does the proposed API look like?"
375 |           description="Describe how you propose to solve the problem and provide code samples of how the API would work once implemented."
376 |           name="api"
377 |           required
378 |           decorator={[FormItem]}
379 |           component={[MdInput]}
380 |         />
381 |       </VoidField>
382 |       <FormButtonGroup.Sticky align="center">
383 |         <Submit
384 |           size="large"
385 |           onSubmit={(values) => {
386 |             window.open(createIssueURL(values))
387 |           }}
388 |         >
389 |           Submit
390 |         </Submit>
391 |       </FormButtonGroup.Sticky>
392 |     </Form>
393 |   )
394 | }
395 | ```
396 | 
```

--------------------------------------------------------------------------------
/packages/reactive/src/__tests__/batch.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { observable, batch, autorun, reaction } from '..'
  2 | import { define } from '../model'
  3 | 
  4 | describe('normal batch', () => {
  5 |   test('no batch', () => {
  6 |     const obs = observable({
  7 |       aa: {
  8 |         bb: 123,
  9 |       },
 10 |     })
 11 |     const handler = jest.fn()
 12 |     autorun(() => {
 13 |       handler(obs.aa.bb)
 14 |     })
 15 |     obs.aa.bb = 111
 16 |     obs.aa.bb = 222
 17 |     expect(handler).toBeCalledTimes(3)
 18 | 
 19 |     obs.aa.bb = 333
 20 |     obs.aa.bb = 444
 21 | 
 22 |     expect(handler).toBeCalledTimes(5)
 23 |   })
 24 | 
 25 |   test('batch', () => {
 26 |     const obs = observable({
 27 |       aa: {
 28 |         bb: 123,
 29 |       },
 30 |     })
 31 |     const handler = jest.fn()
 32 |     autorun(() => {
 33 |       handler(obs.aa.bb)
 34 |     })
 35 |     obs.aa.bb = 111
 36 |     obs.aa.bb = 222
 37 |     expect(handler).toBeCalledTimes(3)
 38 |     expect(handler).lastCalledWith(222)
 39 |     batch(() => {
 40 |       obs.aa.bb = 333
 41 |       obs.aa.bb = 444
 42 |     })
 43 |     batch(() => {})
 44 |     batch()
 45 |     expect(handler).toBeCalledTimes(4)
 46 |     expect(handler).lastCalledWith(444)
 47 |   })
 48 | 
 49 |   test('batch track', () => {
 50 |     const obs = observable({
 51 |       aa: {
 52 |         bb: 123,
 53 |       },
 54 |       cc: 1,
 55 |     })
 56 |     const handler = jest.fn()
 57 |     autorun(() => {
 58 |       batch(() => {
 59 |         if (obs.cc > 0) {
 60 |           handler(obs.aa.bb)
 61 |           obs.cc = obs.cc + 20
 62 |         }
 63 |       })
 64 |     })
 65 |     expect(handler).toBeCalledTimes(1)
 66 |     expect(obs.cc).toEqual(21)
 67 |     obs.aa.bb = 321
 68 |     expect(handler).toBeCalledTimes(2)
 69 |     expect(obs.cc).toEqual(41)
 70 |   })
 71 | 
 72 |   test('batch.bound', () => {
 73 |     const obs = observable({
 74 |       aa: {
 75 |         bb: 123,
 76 |       },
 77 |     })
 78 |     const handler = jest.fn()
 79 |     const setData = batch.bound(() => {
 80 |       obs.aa.bb = 333
 81 |       obs.aa.bb = 444
 82 |     })
 83 |     autorun(() => {
 84 |       handler(obs.aa.bb)
 85 |     })
 86 |     obs.aa.bb = 111
 87 |     obs.aa.bb = 222
 88 |     expect(handler).toBeCalledTimes(3)
 89 |     expect(handler).lastCalledWith(222)
 90 |     setData()
 91 |     batch(() => {})
 92 |     expect(handler).toBeCalledTimes(4)
 93 |     expect(handler).lastCalledWith(444)
 94 |   })
 95 | 
 96 |   test('batch.bound track', () => {
 97 |     const obs = observable({
 98 |       aa: {
 99 |         bb: 123,
100 |       },
101 |       cc: 1,
102 |     })
103 |     const handler = jest.fn()
104 |     autorun(() => {
105 |       batch.bound(() => {
106 |         if (obs.cc > 0) {
107 |           handler(obs.aa.bb)
108 |           obs.cc = obs.cc + 20
109 |         }
110 |       })()
111 |     })
112 |     expect(handler).toBeCalledTimes(1)
113 |     expect(obs.cc).toEqual(21)
114 |     obs.aa.bb = 321
115 |     expect(handler).toBeCalledTimes(2)
116 |     expect(obs.cc).toEqual(41)
117 |   })
118 | 
119 |   test('batch.scope', () => {
120 |     const obs = observable<any>({})
121 | 
122 |     const handler = jest.fn()
123 | 
124 |     autorun(() => {
125 |       handler(obs.aa, obs.bb, obs.cc, obs.dd)
126 |     })
127 | 
128 |     batch(() => {
129 |       batch.scope(() => {
130 |         obs.aa = 123
131 |       })
132 |       batch.scope(() => {
133 |         obs.cc = 'ccccc'
134 |       })
135 |       obs.bb = 321
136 |       obs.dd = 'ddddd'
137 |     })
138 | 
139 |     expect(handler).toBeCalledTimes(4)
140 |     expect(handler).nthCalledWith(1, undefined, undefined, undefined, undefined)
141 |     expect(handler).nthCalledWith(2, 123, undefined, undefined, undefined)
142 |     expect(handler).nthCalledWith(3, 123, undefined, 'ccccc', undefined)
143 |     expect(handler).nthCalledWith(4, 123, 321, 'ccccc', 'ddddd')
144 |   })
145 | 
146 |   test('batch.scope bound', () => {
147 |     const obs = observable<any>({})
148 | 
149 |     const handler = jest.fn()
150 | 
151 |     autorun(() => {
152 |       handler(obs.aa, obs.bb, obs.cc, obs.dd)
153 |     })
154 | 
155 |     const scope1 = batch.scope.bound(() => {
156 |       obs.aa = 123
157 |     })
158 |     batch(() => {
159 |       scope1()
160 |       batch.scope.bound(() => {
161 |         obs.cc = 'ccccc'
162 |       })()
163 |       obs.bb = 321
164 |       obs.dd = 'ddddd'
165 |     })
166 | 
167 |     expect(handler).toBeCalledTimes(4)
168 |     expect(handler).nthCalledWith(1, undefined, undefined, undefined, undefined)
169 |     expect(handler).nthCalledWith(2, 123, undefined, undefined, undefined)
170 |     expect(handler).nthCalledWith(3, 123, undefined, 'ccccc', undefined)
171 |     expect(handler).nthCalledWith(4, 123, 321, 'ccccc', 'ddddd')
172 |   })
173 | 
174 |   test('batch.scope track', () => {
175 |     const obs = observable({
176 |       aa: {
177 |         bb: 123,
178 |       },
179 |       cc: 1,
180 |     })
181 |     const handler = jest.fn()
182 |     autorun(() => {
183 |       batch.scope(() => {
184 |         if (obs.cc > 0) {
185 |           handler(obs.aa.bb)
186 |           obs.cc = obs.cc + 20
187 |         }
188 |       })
189 |     })
190 |     expect(handler).toBeCalledTimes(1)
191 |     expect(obs.cc).toEqual(21)
192 |     obs.aa.bb = 321
193 |     expect(handler).toBeCalledTimes(2)
194 |     expect(obs.cc).toEqual(41)
195 |   })
196 | 
197 |   test('batch.scope bound track', () => {
198 |     const obs = observable({
199 |       aa: {
200 |         bb: 123,
201 |       },
202 |       cc: 1,
203 |     })
204 |     const handler = jest.fn()
205 |     autorun(() => {
206 |       batch.scope.bound(() => {
207 |         if (obs.cc > 0) {
208 |           handler(obs.aa.bb)
209 |           obs.cc = obs.cc + 20
210 |         }
211 |       })()
212 |     })
213 |     expect(handler).toBeCalledTimes(1)
214 |     expect(obs.cc).toEqual(21)
215 |     obs.aa.bb = 321
216 |     expect(handler).toBeCalledTimes(2)
217 |     expect(obs.cc).toEqual(41)
218 |   })
219 | 
220 |   test('batch error', () => {
221 |     let error = null
222 |     try {
223 |       batch(() => {
224 |         throw '123'
225 |       })
226 |     } catch (e) {
227 |       error = e
228 |     }
229 |     expect(error).toEqual('123')
230 |   })
231 | })
232 | 
233 | describe('annotation batch', () => {
234 |   test('batch', () => {
235 |     const obs = define(
236 |       {
237 |         aa: {
238 |           bb: 123,
239 |         },
240 |         setData() {
241 |           this.aa.bb = 333
242 |           this.aa.bb = 444
243 |         },
244 |       },
245 |       {
246 |         aa: observable,
247 |         setData: batch,
248 |       }
249 |     )
250 |     const handler = jest.fn()
251 |     autorun(() => {
252 |       handler(obs.aa.bb)
253 |     })
254 |     obs.aa.bb = 111
255 |     obs.aa.bb = 222
256 |     expect(handler).toBeCalledTimes(3)
257 |     expect(handler).lastCalledWith(222)
258 |     obs.setData()
259 |     expect(handler).toBeCalledTimes(4)
260 |     expect(handler).lastCalledWith(444)
261 |   })
262 | 
263 |   test('batch track', () => {
264 |     const obs = define(
265 |       {
266 |         aa: {
267 |           bb: 123,
268 |         },
269 |         cc: 1,
270 |         setData() {
271 |           if (obs.cc > 0) {
272 |             handler(obs.aa.bb)
273 |             obs.cc = obs.cc + 20
274 |           }
275 |         },
276 |       },
277 |       {
278 |         aa: observable,
279 |         setData: batch,
280 |       }
281 |     )
282 |     const handler = jest.fn()
283 |     autorun(() => {
284 |       obs.setData()
285 |     })
286 |     expect(handler).toBeCalledTimes(1)
287 |     expect(obs.cc).toEqual(21)
288 |     obs.aa.bb = 321
289 |     expect(handler).toBeCalledTimes(2)
290 |     expect(obs.cc).toEqual(41)
291 |   })
292 | 
293 |   test('batch.bound', () => {
294 |     const obs = define(
295 |       {
296 |         aa: {
297 |           bb: 123,
298 |         },
299 |         setData() {
300 |           this.aa.bb = 333
301 |           this.aa.bb = 444
302 |         },
303 |       },
304 |       {
305 |         aa: observable,
306 |         setData: batch.bound,
307 |       }
308 |     )
309 |     const handler = jest.fn()
310 |     autorun(() => {
311 |       handler(obs.aa.bb)
312 |     })
313 |     obs.aa.bb = 111
314 |     obs.aa.bb = 222
315 |     expect(handler).toBeCalledTimes(3)
316 |     expect(handler).lastCalledWith(222)
317 |     obs.setData()
318 |     expect(handler).toBeCalledTimes(4)
319 |     expect(handler).lastCalledWith(444)
320 |   })
321 | 
322 |   test('batch.bound track', () => {
323 |     const obs = define(
324 |       {
325 |         aa: {
326 |           bb: 123,
327 |         },
328 |         cc: 1,
329 |         setData() {
330 |           if (obs.cc > 0) {
331 |             handler(obs.aa.bb)
332 |             obs.cc = obs.cc + 20
333 |           }
334 |         },
335 |       },
336 |       {
337 |         aa: observable,
338 |         setData: batch.bound,
339 |       }
340 |     )
341 |     const handler = jest.fn()
342 |     autorun(() => {
343 |       obs.setData()
344 |     })
345 |     expect(handler).toBeCalledTimes(1)
346 |     expect(obs.cc).toEqual(21)
347 |     obs.aa.bb = 321
348 |     expect(handler).toBeCalledTimes(2)
349 |     expect(obs.cc).toEqual(41)
350 |   })
351 | 
352 |   test('batch.scope', () => {
353 |     const obs = define(
354 |       {
355 |         aa: null,
356 |         bb: null,
357 |         cc: null,
358 |         dd: null,
359 |         scope1() {
360 |           this.aa = 123
361 |         },
362 |         scope2() {
363 |           this.cc = 'ccccc'
364 |         },
365 |       },
366 |       {
367 |         aa: observable,
368 |         bb: observable,
369 |         cc: observable,
370 |         dd: observable,
371 |         scope1: batch.scope,
372 |         scope2: batch.scope,
373 |       }
374 |     )
375 | 
376 |     const handler = jest.fn()
377 | 
378 |     autorun(() => {
379 |       handler(obs.aa, obs.bb, obs.cc, obs.dd)
380 |     })
381 | 
382 |     batch(() => {
383 |       obs.scope1()
384 |       obs.scope2()
385 |       obs.bb = 321
386 |       obs.dd = 'ddddd'
387 |     })
388 | 
389 |     expect(handler).toBeCalledTimes(4)
390 |     expect(handler).nthCalledWith(1, null, null, null, null)
391 |     expect(handler).nthCalledWith(2, 123, null, null, null)
392 |     expect(handler).nthCalledWith(3, 123, null, 'ccccc', null)
393 |     expect(handler).nthCalledWith(4, 123, 321, 'ccccc', 'ddddd')
394 |   })
395 | 
396 |   test('batch.scope bound', () => {
397 |     const obs = define(
398 |       {
399 |         aa: null,
400 |         bb: null,
401 |         cc: null,
402 |         dd: null,
403 |         scope1() {
404 |           this.aa = 123
405 |         },
406 |         scope2() {
407 |           this.cc = 'ccccc'
408 |         },
409 |       },
410 |       {
411 |         aa: observable,
412 |         bb: observable,
413 |         cc: observable,
414 |         dd: observable,
415 |         scope1: batch.scope.bound,
416 |         scope2: batch.scope.bound,
417 |       }
418 |     )
419 | 
420 |     const handler = jest.fn()
421 | 
422 |     autorun(() => {
423 |       handler(obs.aa, obs.bb, obs.cc, obs.dd)
424 |     })
425 | 
426 |     batch(() => {
427 |       obs.scope1()
428 |       obs.scope2()
429 |       obs.bb = 321
430 |       obs.dd = 'ddddd'
431 |     })
432 | 
433 |     expect(handler).toBeCalledTimes(4)
434 |     expect(handler).nthCalledWith(1, null, null, null, null)
435 |     expect(handler).nthCalledWith(2, 123, null, null, null)
436 |     expect(handler).nthCalledWith(3, 123, null, 'ccccc', null)
437 |     expect(handler).nthCalledWith(4, 123, 321, 'ccccc', 'ddddd')
438 |   })
439 | 
440 |   test('batch.scope track', () => {
441 |     const obs = define(
442 |       {
443 |         aa: {
444 |           bb: 123,
445 |         },
446 |         cc: 1,
447 |         scope() {
448 |           if (this.cc > 0) {
449 |             handler(this.aa.bb)
450 |             this.cc = this.cc + 20
451 |           }
452 |         },
453 |       },
454 |       {
455 |         aa: observable,
456 |         cc: observable,
457 |         scope: batch.scope,
458 |       }
459 |     )
460 |     const handler = jest.fn()
461 |     autorun(() => {
462 |       obs.scope()
463 |     })
464 |     expect(handler).toBeCalledTimes(1)
465 |     expect(obs.cc).toEqual(21)
466 |     obs.aa.bb = 321
467 |     expect(handler).toBeCalledTimes(2)
468 |     expect(obs.cc).toEqual(41)
469 |   })
470 | 
471 |   test('batch.scope bound track', () => {
472 |     const obs = define(
473 |       {
474 |         aa: {
475 |           bb: 123,
476 |         },
477 |         cc: 1,
478 |         scope() {
479 |           if (this.cc > 0) {
480 |             handler(this.aa.bb)
481 |             this.cc = this.cc + 20
482 |           }
483 |         },
484 |       },
485 |       {
486 |         aa: observable,
487 |         cc: observable,
488 |         scope: batch.scope.bound,
489 |       }
490 |     )
491 |     const handler = jest.fn()
492 |     autorun(() => {
493 |       obs.scope()
494 |     })
495 |     expect(handler).toBeCalledTimes(1)
496 |     expect(obs.cc).toEqual(21)
497 |     obs.aa.bb = 321
498 |     expect(handler).toBeCalledTimes(2)
499 |     expect(obs.cc).toEqual(41)
500 |   })
501 | })
502 | 
503 | describe('batch endpoint', () => {
504 |   test('normal endpoint', () => {
505 |     const tokens = []
506 |     const inner = batch.bound(() => {
507 |       batch.endpoint(() => {
508 |         tokens.push('endpoint')
509 |       })
510 |       tokens.push('inner')
511 |     })
512 |     const wrapper = batch.bound(() => {
513 |       inner()
514 |       tokens.push('wrapper')
515 |     })
516 |     wrapper()
517 |     expect(tokens).toEqual(['inner', 'wrapper', 'endpoint'])
518 |   })
519 | 
520 |   test('unexpect endpoint', () => {
521 |     const tokens = []
522 |     const inner = batch.bound(() => {
523 |       batch.endpoint()
524 |       tokens.push('inner')
525 |     })
526 |     const wrapper = batch.bound(() => {
527 |       inner()
528 |       tokens.push('wrapper')
529 |     })
530 |     wrapper()
531 |     expect(tokens).toEqual(['inner', 'wrapper'])
532 |   })
533 | 
534 |   test('no wrapper endpoint', () => {
535 |     const tokens = []
536 |     batch.endpoint(() => {
537 |       tokens.push('endpoint')
538 |     })
539 |     expect(tokens).toEqual(['endpoint'])
540 |   })
541 | })
542 | 
543 | test('reaction collect in batch valid', () => {
544 |   const obs = observable({
545 |     aa: 11,
546 |     bb: 22,
547 |     cc: 33,
548 |   })
549 |   reaction(
550 |     () => obs.aa,
551 |     () => {
552 |       void obs.cc
553 |     }
554 |   )
555 |   const fn = jest.fn()
556 | 
557 |   autorun(() => {
558 |     batch.scope(() => {
559 |       obs.aa = obs.bb
560 |     })
561 |     fn()
562 |   })
563 | 
564 |   obs.bb = 44
565 |   expect(fn).toBeCalledTimes(2)
566 | })
567 | 
568 | test('reaction collect in batch invalid', () => {
569 |   const obs = observable({
570 |     aa: 11,
571 |     bb: 22,
572 |     cc: 33,
573 |   })
574 |   reaction(
575 |     () => obs.aa,
576 |     () => {
577 |       void obs.cc
578 |     }
579 |   )
580 |   const fn = jest.fn()
581 | 
582 |   autorun(() => {
583 |     batch.scope(() => {
584 |       obs.aa = obs.bb
585 |     })
586 |     fn()
587 |   })
588 | 
589 |   obs.bb = 44
590 |   obs.cc = 55
591 |   expect(fn).toBeCalledTimes(3)
592 | })
593 | 
```

--------------------------------------------------------------------------------
/packages/antd/docs/components/Select.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Select
  2 | 
  3 | > Drop-down box components
  4 | 
  5 | ## Markup Schema synchronization data source case
  6 | 
  7 | ```tsx
  8 | import React from 'react'
  9 | import { Select, FormItem, FormButtonGroup, Submit } from '@formily/antd'
 10 | import { createForm } from '@formily/core'
 11 | import { FormProvider, createSchemaField } from '@formily/react'
 12 | 
 13 | const SchemaField = createSchemaField({
 14 |   components: {
 15 |     Select,
 16 |     FormItem,
 17 |   },
 18 | })
 19 | 
 20 | const form = createForm()
 21 | 
 22 | export default () => (
 23 |   <FormProvider form={form}>
 24 |     <SchemaField>
 25 |       <SchemaField.Number
 26 |         name="select"
 27 |         title="select box"
 28 |         x-decorator="FormItem"
 29 |         x-component="Select"
 30 |         enum={[
 31 |           { label: 'Option 1', value: 1 },
 32 |           { label: 'Option 2', value: 2 },
 33 |         ]}
 34 |         x-component-props={{
 35 |           style: {
 36 |             width: 120,
 37 |           },
 38 |         }}
 39 |       />
 40 |     </SchemaField>
 41 |     <FormButtonGroup>
 42 |       <Submit onSubmit={console.log}>Submit</Submit>
 43 |     </FormButtonGroup>
 44 |   </FormProvider>
 45 | )
 46 | ```
 47 | 
 48 | ## Markup Schema Asynchronous Search Case
 49 | 
 50 | ```tsx
 51 | import React from 'react'
 52 | import { Select, FormItem, FormButtonGroup, Submit } from '@formily/antd'
 53 | import {
 54 |   createForm,
 55 |   onFieldReact,
 56 |   onFieldInit,
 57 |   FormPathPattern,
 58 |   Field,
 59 | } from '@formily/core'
 60 | import { FormProvider, createSchemaField } from '@formily/react'
 61 | import { action, observable } from '@formily/reactive'
 62 | import { fetch } from 'mfetch'
 63 | 
 64 | let timeout
 65 | let currentValue
 66 | 
 67 | function fetchData(value, callback) {
 68 |   if (timeout) {
 69 |     clearTimeout(timeout)
 70 |     timeout = null
 71 |   }
 72 |   currentValue = value
 73 | 
 74 |   function fake() {
 75 |     fetch(`https://suggest.taobao.com/sug?q=${value}`, {
 76 |       method: 'jsonp',
 77 |     })
 78 |       .then((response) => response.json())
 79 |       .then((d) => {
 80 |         if (currentValue === value) {
 81 |           const { result } = d
 82 |           const data = []
 83 |           result.forEach((r) => {
 84 |             data.push({
 85 |               value: r[0],
 86 |               text: r[0],
 87 |             })
 88 |           })
 89 |           callback(data)
 90 |         }
 91 |       })
 92 |   }
 93 | 
 94 |   timeout = setTimeout(fake, 300)
 95 | }
 96 | 
 97 | const SchemaField = createSchemaField({
 98 |   components: {
 99 |     Select,
100 |     FormItem,
101 |   },
102 | })
103 | 
104 | const useAsyncDataSource = (
105 |   pattern: FormPathPattern,
106 |   service: (param: {
107 |     keyword: string
108 |     field: Field
109 |   }) => Promise<{ label: string; value: any }[]>
110 | ) => {
111 |   const keyword = observable.ref('')
112 | 
113 |   onFieldInit(pattern, (field) => {
114 |     field.setComponentProps({
115 |       onSearch: (value) => {
116 |         keyword.value = value
117 |       },
118 |     })
119 |   })
120 | 
121 |   onFieldReact(pattern, (field) => {
122 |     field.loading = true
123 |     service({ field, keyword: keyword.value }).then(
124 |       action.bound((data) => {
125 |         field.dataSource = data
126 |         field.loading = false
127 |       })
128 |     )
129 |   })
130 | }
131 | 
132 | const form = createForm({
133 |   effects: () => {
134 |     useAsyncDataSource('select', async ({ keyword }) => {
135 |       if (!keyword) {
136 |         return []
137 |       }
138 |       return new Promise((resolve) => {
139 |         fetchData(keyword, resolve)
140 |       })
141 |     })
142 |   },
143 | })
144 | 
145 | export default () => (
146 |   <FormProvider form={form}>
147 |     <SchemaField>
148 |       <SchemaField.String
149 |         name="select"
150 |         title="Asynchronous search select box"
151 |         x-decorator="FormItem"
152 |         x-component="Select"
153 |         x-component-props={{
154 |           showSearch: true,
155 |           filterOption: false,
156 |           style: {
157 |             width: 300,
158 |           },
159 |         }}
160 |       />
161 |     </SchemaField>
162 |     <FormButtonGroup>
163 |       <Submit onSubmit={console.log}>Submit</Submit>
164 |     </FormButtonGroup>
165 |   </FormProvider>
166 | )
167 | ```
168 | 
169 | ## Markup Schema Asynchronous Linkage Data Source Case
170 | 
171 | ```tsx
172 | import React from 'react'
173 | import { Select, FormItem, FormButtonGroup, Submit } from '@formily/antd'
174 | import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core'
175 | import { FormProvider, createSchemaField } from '@formily/react'
176 | import { action } from '@formily/reactive'
177 | 
178 | const SchemaField = createSchemaField({
179 |   components: {
180 |     Select,
181 |     FormItem,
182 |   },
183 | })
184 | 
185 | const useAsyncDataSource = (
186 |   pattern: FormPathPattern,
187 |   service: (field: Field) => Promise<{ label: string; value: any }[]>
188 | ) => {
189 |   onFieldReact(pattern, (field) => {
190 |     field.loading = true
191 |     service(field).then(
192 |       action.bound((data) => {
193 |         field.dataSource = data
194 |         field.loading = false
195 |       })
196 |     )
197 |   })
198 | }
199 | 
200 | const form = createForm({
201 |   effects: () => {
202 |     useAsyncDataSource('select', async (field) => {
203 |       const linkage = field.query('linkage').get('value')
204 |       if (!linkage) return []
205 |       return new Promise((resolve) => {
206 |         setTimeout(() => {
207 |           if (linkage === 1) {
208 |             resolve([
209 |               {
210 |                 label: 'AAA',
211 |                 value: 'aaa',
212 |               },
213 |               {
214 |                 label: 'BBB',
215 |                 value: 'ccc',
216 |               },
217 |             ])
218 |           } else if (linkage === 2) {
219 |             resolve([
220 |               {
221 |                 label: 'CCC',
222 |                 value: 'ccc',
223 |               },
224 |               {
225 |                 label: 'DDD',
226 |                 value: 'ddd',
227 |               },
228 |             ])
229 |           }
230 |         }, 1500)
231 |       })
232 |     })
233 |   },
234 | })
235 | 
236 | export default () => (
237 |   <FormProvider form={form}>
238 |     <SchemaField>
239 |       <SchemaField.Number
240 |         name="linkage"
241 |         title="Linkage selection box"
242 |         x-decorator="FormItem"
243 |         x-component="Select"
244 |         enum={[
245 |           { label: 'Request 1', value: 1 },
246 |           { label: 'Request 2', value: 2 },
247 |         ]}
248 |         x-component-props={{
249 |           style: {
250 |             width: 120,
251 |           },
252 |         }}
253 |       />
254 |       <SchemaField.String
255 |         name="select"
256 |         title="Asynchronous select box"
257 |         x-decorator="FormItem"
258 |         x-component="Select"
259 |         x-component-props={{
260 |           style: {
261 |             width: 120,
262 |           },
263 |         }}
264 |       />
265 |     </SchemaField>
266 |     <FormButtonGroup>
267 |       <Submit onSubmit={console.log}>Submit</Submit>
268 |     </FormButtonGroup>
269 |   </FormProvider>
270 | )
271 | ```
272 | 
273 | ## JSON Schema synchronization data source case
274 | 
275 | ```tsx
276 | import React from 'react'
277 | import { Select, FormItem, FormButtonGroup, Submit } from '@formily/antd'
278 | import { createForm } from '@formily/core'
279 | import { FormProvider, createSchemaField } from '@formily/react'
280 | 
281 | const SchemaField = createSchemaField({
282 |   components: {
283 |     Select,
284 |     FormItem,
285 |   },
286 | })
287 | 
288 | const form = createForm()
289 | 
290 | const schema = {
291 |   type: 'object',
292 |   properties: {
293 |     select: {
294 |       type: 'string',
295 |       title: 'Select box',
296 |       'x-decorator': 'FormItem',
297 |       'x-component': 'Select',
298 |       enum: [
299 |         { label: 'Option 1', value: 1 },
300 |         { label: 'Option 2', value: 2 },
301 |       ],
302 |       'x-component-props': {
303 |         style: {
304 |           width: 120,
305 |         },
306 |       },
307 |     },
308 |   },
309 | }
310 | 
311 | export default () => (
312 |   <FormProvider form={form}>
313 |     <SchemaField schema={schema} />
314 |     <FormButtonGroup>
315 |       <Submit onSubmit={console.log}>Submit</Submit>
316 |     </FormButtonGroup>
317 |   </FormProvider>
318 | )
319 | ```
320 | 
321 | ## JSON Schema asynchronous linkage data source case
322 | 
323 | ```tsx
324 | import React from 'react'
325 | import { Select, FormItem, FormButtonGroup, Submit } from '@formily/antd'
326 | import { createForm } from '@formily/core'
327 | import { FormProvider, createSchemaField } from '@formily/react'
328 | import { action } from '@formily/reactive'
329 | 
330 | const SchemaField = createSchemaField({
331 |   components: {
332 |     Select,
333 |     FormItem,
334 |   },
335 | })
336 | 
337 | const loadData = async (field) => {
338 |   const linkage = field.query('linkage').get('value')
339 |   if (!linkage) return []
340 |   return new Promise((resolve) => {
341 |     setTimeout(() => {
342 |       if (linkage === 1) {
343 |         resolve([
344 |           {
345 |             label: 'AAA',
346 |             value: 'aaa',
347 |           },
348 |           {
349 |             label: 'BBB',
350 |             value: 'ccc',
351 |           },
352 |         ])
353 |       } else if (linkage === 2) {
354 |         resolve([
355 |           {
356 |             label: 'CCC',
357 |             value: 'ccc',
358 |           },
359 |           {
360 |             label: 'DDD',
361 |             value: 'ddd',
362 |           },
363 |         ])
364 |       }
365 |     }, 1500)
366 |   })
367 | }
368 | 
369 | const useAsyncDataSource = (service) => (field) => {
370 |   field.loading = true
371 |   service(field).then(
372 |     action.bound((data) => {
373 |       field.dataSource = data
374 |       field.loading = false
375 |     })
376 |   )
377 | }
378 | 
379 | const form = createForm()
380 | 
381 | const schema = {
382 |   type: 'object',
383 |   properties: {
384 |     linkage: {
385 |       type: 'string',
386 |       title: 'Linkage selection box',
387 |       enum: [
388 |         { label: 'Request 1', value: 1 },
389 |         { label: 'Request 2', value: 2 },
390 |       ],
391 |       'x-decorator': 'FormItem',
392 |       'x-component': 'Select',
393 |       'x-component-props': {
394 |         style: {
395 |           width: 120,
396 |         },
397 |       },
398 |     },
399 |     select: {
400 |       type: 'string',
401 |       title: 'Asynchronous selection box',
402 |       'x-decorator': 'FormItem',
403 |       'x-component': 'Select',
404 |       'x-component-props': {
405 |         style: {
406 |           width: 120,
407 |         },
408 |       },
409 |       'x-reactions': ['{{useAsyncDataSource(loadData)}}'],
410 |     },
411 |   },
412 | }
413 | 
414 | export default () => (
415 |   <FormProvider form={form}>
416 |     <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} />
417 |     <FormButtonGroup>
418 |       <Submit onSubmit={console.log}>Submit</Submit>
419 |     </FormButtonGroup>
420 |   </FormProvider>
421 | )
422 | ```
423 | 
424 | ## Pure JSX synchronization data source case
425 | 
426 | ```tsx
427 | import React from 'react'
428 | import { Select, FormItem, FormButtonGroup, Submit } from '@formily/antd'
429 | import { createForm } from '@formily/core'
430 | import { FormProvider, Field } from '@formily/react'
431 | 
432 | const form = createForm()
433 | 
434 | export default () => (
435 |   <FormProvider form={form}>
436 |     <Field
437 |       name="select"
438 |       title="select box"
439 |       dataSource={[
440 |         { label: 'Option 1', value: 1 },
441 |         { label: 'Option 2', value: 2 },
442 |       ]}
443 |       decorator={[FormItem]}
444 |       component={[
445 |         Select,
446 |         {
447 |           style: {
448 |             width: 120,
449 |           },
450 |         },
451 |       ]}
452 |     />
453 |     <FormButtonGroup>
454 |       <Submit onSubmit={console.log}>Submit</Submit>
455 |     </FormButtonGroup>
456 |   </FormProvider>
457 | )
458 | ```
459 | 
460 | ## Pure JSX asynchronous linkage data source case
461 | 
462 | ```tsx
463 | import React from 'react'
464 | import { Select, FormItem, FormButtonGroup, Submit } from '@formily/antd'
465 | import {
466 |   createForm,
467 |   onFieldReact,
468 |   FormPathPattern,
469 |   Field as FieldType,
470 | } from '@formily/core'
471 | import { FormProvider, Field } from '@formily/react'
472 | import { action } from '@formily/reactive'
473 | 
474 | const useAsyncDataSource = (
475 |   pattern: FormPathPattern,
476 |   service: (field: FieldType) => Promise<{ label: string; value: any }[]>
477 | ) => {
478 |   onFieldReact(pattern, (field) => {
479 |     field.loading = true
480 |     service(field).then(
481 |       action.bound((data) => {
482 |         field.dataSource = data
483 |         field.loading = false
484 |       })
485 |     )
486 |   })
487 | }
488 | 
489 | const form = createForm({
490 |   effects: () => {
491 |     useAsyncDataSource('select', async (field) => {
492 |       const linkage = field.query('linkage').get('value')
493 |       if (!linkage) return []
494 |       return new Promise((resolve) => {
495 |         setTimeout(() => {
496 |           if (linkage === 1) {
497 |             resolve([
498 |               {
499 |                 label: 'AAA',
500 |                 value: 'aaa',
501 |               },
502 |               {
503 |                 label: 'BBB',
504 |                 value: 'ccc',
505 |               },
506 |             ])
507 |           } else if (linkage === 2) {
508 |             resolve([
509 |               {
510 |                 label: 'CCC',
511 |                 value: 'ccc',
512 |               },
513 |               {
514 |                 label: 'DDD',
515 |                 value: 'ddd',
516 |               },
517 |             ])
518 |           }
519 |         }, 1500)
520 |       })
521 |     })
522 |   },
523 | })
524 | 
525 | export default () => (
526 |   <FormProvider form={form}>
527 |     <Field
528 |       name="linkage"
529 |       title="Linkage selection box"
530 |       dataSource={[
531 |         { label: 'Request 1', value: 1 },
532 |         { label: 'Request 2', value: 2 },
533 |       ]}
534 |       decorator={[FormItem]}
535 |       component={[
536 |         Select,
537 |         {
538 |           style: {
539 |             width: 120,
540 |           },
541 |         },
542 |       ]}
543 |     />
544 |     <Field
545 |       name="select"
546 |       title="Asynchronous select box"
547 |       decorator={[FormItem]}
548 |       component={[
549 |         Select,
550 |         {
551 |           style: {
552 |             width: 120,
553 |           },
554 |         },
555 |       ]}
556 |     />
557 |     <FormButtonGroup>
558 |       <Submit onSubmit={console.log}>Submit</Submit>
559 |     </FormButtonGroup>
560 |   </FormProvider>
561 | )
562 | ```
563 | 
564 | ## API
565 | 
566 | Reference https://ant.design/components/select-cn/
567 | 
```

--------------------------------------------------------------------------------
/packages/next/docs/components/ArrayCards.zh-CN.md:
--------------------------------------------------------------------------------

```markdown
  1 | # ArrayCards
  2 | 
  3 | > 卡片列表,对于每行字段数量较多,联动较多的场景比较适合使用 ArrayCards
  4 | >
  5 | > 注意:该组件只适用于 Schema 场景
  6 | 
  7 | ## Markup Schema 案例
  8 | 
  9 | ```tsx
 10 | import React from 'react'
 11 | import {
 12 |   FormItem,
 13 |   Input,
 14 |   ArrayCards,
 15 |   FormButtonGroup,
 16 |   Submit,
 17 | } from '@formily/next'
 18 | import { createForm } from '@formily/core'
 19 | import { FormProvider, createSchemaField } from '@formily/react'
 20 | 
 21 | const SchemaField = createSchemaField({
 22 |   components: {
 23 |     FormItem,
 24 |     Input,
 25 |     ArrayCards,
 26 |   },
 27 | })
 28 | 
 29 | const form = createForm()
 30 | 
 31 | export default () => {
 32 |   return (
 33 |     <FormProvider form={form}>
 34 |       <SchemaField>
 35 |         <SchemaField.Array
 36 |           name="string_array"
 37 |           maxItems={3}
 38 |           x-decorator="FormItem"
 39 |           x-component="ArrayCards"
 40 |           x-component-props={{
 41 |             title: '字符串数组',
 42 |           }}
 43 |         >
 44 |           <SchemaField.Void>
 45 |             <SchemaField.Void x-component="ArrayCards.Index" />
 46 |             <SchemaField.String
 47 |               name="input"
 48 |               x-decorator="FormItem"
 49 |               title="Input"
 50 |               required
 51 |               x-component="Input"
 52 |             />
 53 |             <SchemaField.Void x-component="ArrayCards.Remove" />
 54 |             <SchemaField.Void x-component="ArrayCards.Copy" />
 55 |             <SchemaField.Void x-component="ArrayCards.MoveUp" />
 56 |             <SchemaField.Void x-component="ArrayCards.MoveDown" />
 57 |           </SchemaField.Void>
 58 |           <SchemaField.Void
 59 |             x-component="ArrayCards.Addition"
 60 |             title="添加条目"
 61 |           />
 62 |         </SchemaField.Array>
 63 |         <SchemaField.Array
 64 |           name="array"
 65 |           maxItems={3}
 66 |           x-decorator="FormItem"
 67 |           x-component="ArrayCards"
 68 |           x-component-props={{
 69 |             title: '对象数组',
 70 |           }}
 71 |         >
 72 |           <SchemaField.Object>
 73 |             <SchemaField.Void x-component="ArrayCards.Index" />
 74 |             <SchemaField.String
 75 |               name="input"
 76 |               x-decorator="FormItem"
 77 |               title="Input"
 78 |               required
 79 |               x-component="Input"
 80 |             />
 81 |             <SchemaField.Void x-component="ArrayCards.Remove" />
 82 |             <SchemaField.Void x-component="ArrayCards.MoveUp" />
 83 |             <SchemaField.Void x-component="ArrayCards.MoveDown" />
 84 |           </SchemaField.Object>
 85 |           <SchemaField.Void
 86 |             x-component="ArrayCards.Addition"
 87 |             title="添加条目"
 88 |           />
 89 |         </SchemaField.Array>
 90 |       </SchemaField>
 91 |       <FormButtonGroup>
 92 |         <Submit onSubmit={console.log}>提交</Submit>
 93 |       </FormButtonGroup>
 94 |     </FormProvider>
 95 |   )
 96 | }
 97 | ```
 98 | 
 99 | ## JSON Schema 案例
100 | 
101 | ```tsx
102 | import React from 'react'
103 | import {
104 |   FormItem,
105 |   Input,
106 |   ArrayCards,
107 |   FormButtonGroup,
108 |   Submit,
109 | } from '@formily/next'
110 | import { createForm } from '@formily/core'
111 | import { FormProvider, createSchemaField } from '@formily/react'
112 | 
113 | const SchemaField = createSchemaField({
114 |   components: {
115 |     FormItem,
116 |     Input,
117 |     ArrayCards,
118 |   },
119 | })
120 | 
121 | const form = createForm()
122 | 
123 | const schema = {
124 |   type: 'object',
125 |   properties: {
126 |     string_array: {
127 |       type: 'array',
128 |       'x-component': 'ArrayCards',
129 |       maxItems: 3,
130 |       'x-decorator': 'FormItem',
131 |       'x-component-props': {
132 |         title: '字符串数组',
133 |       },
134 |       items: {
135 |         type: 'void',
136 |         properties: {
137 |           index: {
138 |             type: 'void',
139 |             'x-component': 'ArrayCards.Index',
140 |           },
141 |           input: {
142 |             type: 'string',
143 |             'x-decorator': 'FormItem',
144 |             title: 'Input',
145 |             required: true,
146 |             'x-component': 'Input',
147 |           },
148 |           remove: {
149 |             type: 'void',
150 |             'x-component': 'ArrayCards.Remove',
151 |           },
152 |           moveUp: {
153 |             type: 'void',
154 |             'x-component': 'ArrayCards.MoveUp',
155 |           },
156 |           moveDown: {
157 |             type: 'void',
158 |             'x-component': 'ArrayCards.MoveDown',
159 |           },
160 |         },
161 |       },
162 |       properties: {
163 |         addition: {
164 |           type: 'void',
165 |           title: '添加条目',
166 |           'x-component': 'ArrayCards.Addition',
167 |         },
168 |       },
169 |     },
170 |     array: {
171 |       type: 'array',
172 |       'x-component': 'ArrayCards',
173 |       maxItems: 3,
174 |       'x-decorator': 'FormItem',
175 |       'x-component-props': {
176 |         title: '对象数组',
177 |       },
178 |       items: {
179 |         type: 'object',
180 |         properties: {
181 |           index: {
182 |             type: 'void',
183 |             'x-component': 'ArrayCards.Index',
184 |           },
185 |           input: {
186 |             type: 'string',
187 |             'x-decorator': 'FormItem',
188 |             title: 'Input',
189 |             required: true,
190 |             'x-component': 'Input',
191 |           },
192 |           remove: {
193 |             type: 'void',
194 |             'x-component': 'ArrayCards.Remove',
195 |           },
196 |           moveUp: {
197 |             type: 'void',
198 |             'x-component': 'ArrayCards.MoveUp',
199 |           },
200 |           moveDown: {
201 |             type: 'void',
202 |             'x-component': 'ArrayCards.MoveDown',
203 |           },
204 |         },
205 |       },
206 |       properties: {
207 |         addition: {
208 |           type: 'void',
209 |           title: '添加条目',
210 |           'x-component': 'ArrayCards.Addition',
211 |         },
212 |       },
213 |     },
214 |   },
215 | }
216 | 
217 | export default () => {
218 |   return (
219 |     <FormProvider form={form}>
220 |       <SchemaField schema={schema} />
221 |       <FormButtonGroup>
222 |         <Submit onSubmit={console.log}>提交</Submit>
223 |       </FormButtonGroup>
224 |     </FormProvider>
225 |   )
226 | }
227 | ```
228 | 
229 | ## Effects 联动案例
230 | 
231 | ```tsx
232 | import React from 'react'
233 | import {
234 |   FormItem,
235 |   Input,
236 |   ArrayCards,
237 |   FormButtonGroup,
238 |   Submit,
239 | } from '@formily/next'
240 | import { createForm, onFieldChange, onFieldReact } from '@formily/core'
241 | import { FormProvider, createSchemaField } from '@formily/react'
242 | 
243 | const SchemaField = createSchemaField({
244 |   components: {
245 |     FormItem,
246 |     Input,
247 |     ArrayCards,
248 |   },
249 | })
250 | 
251 | const form = createForm({
252 |   effects: () => {
253 |     //主动联动模式
254 |     onFieldChange('array.*.aa', ['value'], (field, form) => {
255 |       form.setFieldState(field.query('.bb'), (state) => {
256 |         state.visible = field.value != '123'
257 |       })
258 |     })
259 |     //被动联动模式
260 |     onFieldReact('array.*.dd', (field) => {
261 |       field.visible = field.query('.cc').get('value') != '123'
262 |     })
263 |   },
264 | })
265 | 
266 | export default () => {
267 |   return (
268 |     <FormProvider form={form}>
269 |       <SchemaField>
270 |         <SchemaField.Array
271 |           name="array"
272 |           maxItems={3}
273 |           x-component="ArrayCards"
274 |           x-decorator="FormItem"
275 |           x-component-props={{
276 |             title: '对象数组',
277 |           }}
278 |         >
279 |           <SchemaField.Object>
280 |             <SchemaField.Void x-component="ArrayCards.Index" />
281 |             <SchemaField.String
282 |               name="aa"
283 |               x-decorator="FormItem"
284 |               title="AA"
285 |               required
286 |               description="AA输入123时隐藏BB"
287 |               x-component="Input"
288 |             />
289 |             <SchemaField.String
290 |               name="bb"
291 |               x-decorator="FormItem"
292 |               title="BB"
293 |               required
294 |               x-component="Input"
295 |             />
296 |             <SchemaField.String
297 |               name="cc"
298 |               x-decorator="FormItem"
299 |               title="CC"
300 |               required
301 |               description="CC输入123时隐藏DD"
302 |               x-component="Input"
303 |             />
304 |             <SchemaField.String
305 |               name="dd"
306 |               x-decorator="FormItem"
307 |               title="DD"
308 |               required
309 |               x-component="Input"
310 |             />
311 |             <SchemaField.Void x-component="ArrayCards.Remove" />
312 |             <SchemaField.Void x-component="ArrayCards.MoveUp" />
313 |             <SchemaField.Void x-component="ArrayCards.MoveDown" />
314 |           </SchemaField.Object>
315 |           <SchemaField.Void
316 |             x-component="ArrayCards.Addition"
317 |             title="添加条目"
318 |           />
319 |         </SchemaField.Array>
320 |       </SchemaField>
321 |       <FormButtonGroup>
322 |         <Submit onSubmit={console.log}>提交</Submit>
323 |       </FormButtonGroup>
324 |     </FormProvider>
325 |   )
326 | }
327 | ```
328 | 
329 | ## JSON Schema 联动案例
330 | 
331 | ```tsx
332 | import React from 'react'
333 | import {
334 |   FormItem,
335 |   Input,
336 |   ArrayCards,
337 |   FormButtonGroup,
338 |   Submit,
339 | } from '@formily/next'
340 | import { createForm } from '@formily/core'
341 | import { FormProvider, createSchemaField } from '@formily/react'
342 | 
343 | const SchemaField = createSchemaField({
344 |   components: {
345 |     FormItem,
346 |     Input,
347 |     ArrayCards,
348 |   },
349 | })
350 | 
351 | const form = createForm()
352 | 
353 | const schema = {
354 |   type: 'object',
355 |   properties: {
356 |     array: {
357 |       type: 'array',
358 |       'x-component': 'ArrayCards',
359 |       maxItems: 3,
360 |       title: '对象数组',
361 |       items: {
362 |         type: 'object',
363 |         properties: {
364 |           index: {
365 |             type: 'void',
366 |             'x-component': 'ArrayCards.Index',
367 |           },
368 |           aa: {
369 |             type: 'string',
370 |             'x-decorator': 'FormItem',
371 |             title: 'AA',
372 |             required: true,
373 |             'x-component': 'Input',
374 |             description: '输入123',
375 |           },
376 |           bb: {
377 |             type: 'string',
378 |             title: 'BB',
379 |             required: true,
380 |             'x-decorator': 'FormItem',
381 |             'x-component': 'Input',
382 |             'x-reactions': [
383 |               {
384 |                 dependencies: ['.aa'],
385 |                 when: "{{$deps[0] != '123'}}",
386 |                 fulfill: {
387 |                   schema: {
388 |                     title: 'BB',
389 |                     'x-disabled': true,
390 |                   },
391 |                 },
392 |                 otherwise: {
393 |                   schema: {
394 |                     title: 'Changed',
395 |                     'x-disabled': false,
396 |                   },
397 |                 },
398 |               },
399 |             ],
400 |           },
401 |           remove: {
402 |             type: 'void',
403 |             'x-component': 'ArrayCards.Remove',
404 |           },
405 |           moveUp: {
406 |             type: 'void',
407 |             'x-component': 'ArrayCards.MoveUp',
408 |           },
409 |           moveDown: {
410 |             type: 'void',
411 |             'x-component': 'ArrayCards.MoveDown',
412 |           },
413 |         },
414 |       },
415 |       properties: {
416 |         addition: {
417 |           type: 'void',
418 |           title: '添加条目',
419 |           'x-component': 'ArrayCards.Addition',
420 |         },
421 |       },
422 |     },
423 |   },
424 | }
425 | 
426 | export default () => {
427 |   return (
428 |     <FormProvider form={form}>
429 |       <SchemaField schema={schema} />
430 |       <FormButtonGroup>
431 |         <Submit onSubmit={console.log}>提交</Submit>
432 |       </FormButtonGroup>
433 |     </FormProvider>
434 |   )
435 | }
436 | ```
437 | 
438 | ## API
439 | 
440 | ### ArrayCards
441 | 
442 | 扩展属性
443 | 
444 | | 属性名     | 类型                      | 描述         | 默认值 |
445 | | ---------- | ------------------------- | ------------ | ------ |
446 | | onAdd      | `(index: number) => void` | 增加方法     |        |
447 | | onRemove   | `(index: number) => void` | 删除方法     |        |
448 | | onCopy     | `(index: number) => void` | 复制方法     |        |
449 | | onMoveUp   | `(index: number) => void` | 向上移动方法 |        |
450 | | onMoveDown | `(index: number) => void` | 向下移动方法 |        |
451 | 
452 | 其余参考 https://fusion.design/pc/component/basic/card
453 | 
454 | ### ArrayCards.Addition
455 | 
456 | > 添加按钮
457 | 
458 | 扩展属性
459 | 
460 | | 属性名       | 类型                  | 描述     | 默认值   |
461 | | ------------ | --------------------- | -------- | -------- |
462 | | title        | ReactText             | 文案     |          |
463 | | method       | `'push' \| 'unshift'` | 添加方式 | `'push'` |
464 | | defaultValue | `any`                 | 默认值   |          |
465 | 
466 | 其余参考 https://fusion.design/pc/component/basic/button
467 | 
468 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
469 | 
470 | ### ArrayCards.Copy
471 | 
472 | > 复制按钮
473 | 
474 | 扩展属性
475 | 
476 | | 属性名 | 类型                  | 描述     | 默认值   |
477 | | ------ | --------------------- | -------- | -------- |
478 | | title  | ReactText             | 文案     |          |
479 | | method | `'push' \| 'unshift'` | 添加方式 | `'push'` |
480 | 
481 | 其余参考 https://fusion.design/pc/component/basic/button
482 | 
483 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
484 | 
485 | ### ArrayCards.Remove
486 | 
487 | > 删除按钮
488 | 
489 | | 属性名 | 类型      | 描述 | 默认值 |
490 | | ------ | --------- | ---- | ------ |
491 | | title  | ReactText | 文案 |        |
492 | 
493 | 其余参考 https://ant.design/components/icon-cn/
494 | 
495 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
496 | 
497 | ### ArrayCards.MoveDown
498 | 
499 | > 下移按钮
500 | 
501 | | 属性名 | 类型      | 描述 | 默认值 |
502 | | ------ | --------- | ---- | ------ |
503 | | title  | ReactText | 文案 |        |
504 | 
505 | 其余参考 https://ant.design/components/icon-cn/
506 | 
507 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
508 | 
509 | ### ArrayCards.MoveUp
510 | 
511 | > 上移按钮
512 | 
513 | | 属性名 | 类型      | 描述 | 默认值 |
514 | | ------ | --------- | ---- | ------ |
515 | | title  | ReactText | 文案 |        |
516 | 
517 | 其余参考 https://ant.design/components/icon-cn/
518 | 
519 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
520 | 
521 | ### ArrayCards.Index
522 | 
523 | > 索引渲染器
524 | 
525 | 无属性
526 | 
527 | ### ArrayCards.useIndex
528 | 
529 | > 读取当前渲染行索引的 React Hook
530 | 
531 | ### ArrayCards.useRecord
532 | 
533 | > 读取当前渲染记录的 React Hook
534 | 
```

--------------------------------------------------------------------------------
/packages/antd/src/array-table/index.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, {
  2 |   Fragment,
  3 |   useState,
  4 |   useRef,
  5 |   useEffect,
  6 |   createContext,
  7 |   useContext,
  8 |   useCallback,
  9 | } from 'react'
 10 | import { Table, Pagination, Space, Select, Badge } from 'antd'
 11 | import { PaginationProps } from 'antd/lib/pagination'
 12 | import { TableProps, ColumnProps } from 'antd/lib/table'
 13 | import { SelectProps } from 'antd/lib/select'
 14 | import cls from 'classnames'
 15 | import { GeneralField, FieldDisplayTypes, ArrayField } from '@formily/core'
 16 | import {
 17 |   useField,
 18 |   observer,
 19 |   useFieldSchema,
 20 |   RecursionField,
 21 |   ReactFC,
 22 | } from '@formily/react'
 23 | import { isArr, isBool, isFn } from '@formily/shared'
 24 | import { Schema } from '@formily/json-schema'
 25 | import {
 26 |   usePrefixCls,
 27 |   SortableContainer,
 28 |   SortableElement,
 29 | } from '../__builtins__'
 30 | import { ArrayBase, ArrayBaseMixins, IArrayBaseProps } from '../array-base'
 31 | 
 32 | interface ObservableColumnSource {
 33 |   field: GeneralField
 34 |   columnProps: ColumnProps<any>
 35 |   schema: Schema
 36 |   display: FieldDisplayTypes
 37 |   name: string
 38 | }
 39 | interface IArrayTablePaginationProps extends PaginationProps {
 40 |   dataSource?: any[]
 41 |   showPagination?: boolean
 42 |   children?: (
 43 |     dataSource: any[],
 44 |     pagination: React.ReactNode,
 45 |     options: {
 46 |       startIndex: number
 47 |     }
 48 |   ) => React.ReactElement
 49 | }
 50 | 
 51 | interface IStatusSelectProps extends SelectProps<any> {
 52 |   pageSize?: number
 53 | }
 54 | 
 55 | type ComposedArrayTable = React.FC<
 56 |   React.PropsWithChildren<TableProps<any> & IArrayBaseProps>
 57 | > &
 58 |   ArrayBaseMixins & {
 59 |     Column?: React.FC<React.PropsWithChildren<ColumnProps<any>>>
 60 |   }
 61 | 
 62 | interface PaginationAction {
 63 |   totalPage?: number
 64 |   pageSize?: number
 65 |   showPagination?: boolean
 66 |   changePage?: (page: number) => void
 67 | }
 68 | 
 69 | const SortableRow = SortableElement((props: any) => <tr {...props} />)
 70 | const SortableBody = SortableContainer((props: any) => <tbody {...props} />)
 71 | 
 72 | const isColumnComponent = (schema: Schema) => {
 73 |   return schema['x-component']?.indexOf('Column') > -1
 74 | }
 75 | 
 76 | const isOperationsComponent = (schema: Schema) => {
 77 |   return schema['x-component']?.indexOf('Operations') > -1
 78 | }
 79 | 
 80 | const isAdditionComponent = (schema: Schema) => {
 81 |   return schema['x-component']?.indexOf('Addition') > -1
 82 | }
 83 | 
 84 | const useArrayTableSources = () => {
 85 |   const arrayField = useField()
 86 |   const schema = useFieldSchema()
 87 |   const parseSources = (schema: Schema): ObservableColumnSource[] => {
 88 |     if (
 89 |       isColumnComponent(schema) ||
 90 |       isOperationsComponent(schema) ||
 91 |       isAdditionComponent(schema)
 92 |     ) {
 93 |       if (!schema['x-component-props']?.['dataIndex'] && !schema['name'])
 94 |         return []
 95 |       const name = schema['x-component-props']?.['dataIndex'] || schema['name']
 96 |       const field = arrayField.query(arrayField.address.concat(name)).take()
 97 |       const columnProps =
 98 |         field?.component?.[1] || schema['x-component-props'] || {}
 99 |       const display = field?.display || schema['x-display'] || 'visible'
100 |       return [
101 |         {
102 |           name,
103 |           display,
104 |           field,
105 |           schema,
106 |           columnProps,
107 |         },
108 |       ]
109 |     } else if (schema.properties) {
110 |       return schema.reduceProperties((buf, schema) => {
111 |         return buf.concat(parseSources(schema))
112 |       }, [])
113 |     }
114 |   }
115 | 
116 |   const parseArrayItems = (schema: Schema['items']) => {
117 |     if (!schema) return []
118 |     const sources: ObservableColumnSource[] = []
119 |     const items = isArr(schema) ? schema : [schema]
120 |     return items.reduce((columns, schema) => {
121 |       const item = parseSources(schema)
122 |       if (item) {
123 |         return columns.concat(item)
124 |       }
125 |       return columns
126 |     }, sources)
127 |   }
128 | 
129 |   if (!schema) throw new Error('can not found schema object')
130 | 
131 |   return parseArrayItems(schema.items)
132 | }
133 | 
134 | const useArrayTableColumns = (
135 |   dataSource: any[],
136 |   field: ArrayField,
137 |   sources: ObservableColumnSource[]
138 | ): TableProps<any>['columns'] => {
139 |   return sources.reduce((buf, { name, columnProps, schema, display }, key) => {
140 |     if (display !== 'visible') return buf
141 |     if (!isColumnComponent(schema)) return buf
142 |     return buf.concat({
143 |       ...columnProps,
144 |       key,
145 |       dataIndex: name,
146 |       render: (value: any, record: any) => {
147 |         const index = dataSource?.indexOf(record)
148 |         const children = (
149 |           <ArrayBase.Item index={index} record={() => field?.value?.[index]}>
150 |             <RecursionField schema={schema} name={index} onlyRenderProperties />
151 |           </ArrayBase.Item>
152 |         )
153 |         return children
154 |       },
155 |     })
156 |   }, [])
157 | }
158 | 
159 | const useAddition = () => {
160 |   const schema = useFieldSchema()
161 |   return schema.reduceProperties((addition, schema, key) => {
162 |     if (isAdditionComponent(schema)) {
163 |       return <RecursionField schema={schema} name={key} />
164 |     }
165 |     return addition
166 |   }, null)
167 | }
168 | 
169 | const schedulerRequest = {
170 |   request: null,
171 | }
172 | 
173 | const StatusSelect: ReactFC<IStatusSelectProps> = observer(
174 |   (props) => {
175 |     const field = useField<ArrayField>()
176 |     const prefixCls = usePrefixCls('formily-array-table')
177 |     const errors = field.errors
178 |     const parseIndex = (address: string) => {
179 |       return Number(
180 |         address
181 |           .slice(address.indexOf(field.address.toString()) + 1)
182 |           .match(/(\d+)/)?.[1]
183 |       )
184 |     }
185 |     const options = props.options?.map(({ label, value }) => {
186 |       const val = Number(value)
187 |       const hasError = errors.some(({ address }) => {
188 |         const currentIndex = parseIndex(address)
189 |         const startIndex = (val - 1) * props.pageSize
190 |         const endIndex = val * props.pageSize
191 |         return currentIndex >= startIndex && currentIndex <= endIndex
192 |       })
193 |       return {
194 |         label: hasError ? <Badge dot>{label}</Badge> : label,
195 |         value,
196 |       }
197 |     })
198 | 
199 |     const width = String(options?.length).length * 15
200 | 
201 |     return (
202 |       <Select
203 |         value={props.value}
204 |         onChange={props.onChange}
205 |         options={options}
206 |         virtual
207 |         style={{
208 |           width: width < 60 ? 60 : width,
209 |         }}
210 |         className={cls(`${prefixCls}-status-select`, {
211 |           'has-error': errors?.length,
212 |         })}
213 |       />
214 |     )
215 |   },
216 |   {
217 |     scheduler: (update) => {
218 |       clearTimeout(schedulerRequest.request)
219 |       schedulerRequest.request = setTimeout(() => {
220 |         update()
221 |       }, 100)
222 |     },
223 |   }
224 | )
225 | 
226 | const PaginationContext = createContext<PaginationAction>({})
227 | const usePagination = () => {
228 |   return useContext(PaginationContext)
229 | }
230 | 
231 | const ArrayTablePagination: ReactFC<IArrayTablePaginationProps> = (props) => {
232 |   const [current, setCurrent] = useState(1)
233 |   const prefixCls = usePrefixCls('formily-array-table')
234 |   const showPagination = props.showPagination ?? true
235 |   const pageSize = props.pageSize || 10
236 |   const size = props.size || 'default'
237 |   const dataSource = props.dataSource || []
238 |   const startIndex = (current - 1) * pageSize
239 |   const endIndex = startIndex + pageSize - 1
240 |   const total = dataSource?.length || 0
241 |   const totalPage = Math.ceil(total / pageSize)
242 |   const pages = Array.from(new Array(totalPage)).map((_, index) => {
243 |     const page = index + 1
244 |     return {
245 |       label: page,
246 |       value: page,
247 |     }
248 |   })
249 |   const handleChange = (current: number) => {
250 |     setCurrent(current)
251 |   }
252 | 
253 |   useEffect(() => {
254 |     if (totalPage > 0 && totalPage < current) {
255 |       handleChange(totalPage)
256 |     }
257 |   }, [totalPage, current])
258 | 
259 |   const renderPagination = () => {
260 |     if (totalPage <= 1 || !showPagination) return
261 |     return (
262 |       <div className={`${prefixCls}-pagination`}>
263 |         <Space>
264 |           <StatusSelect
265 |             value={current}
266 |             pageSize={pageSize}
267 |             onChange={handleChange}
268 |             options={pages}
269 |             notFoundContent={false}
270 |           />
271 |           <Pagination
272 |             {...props}
273 |             pageSize={pageSize}
274 |             current={current}
275 |             total={dataSource.length}
276 |             size={size}
277 |             showSizeChanger={false}
278 |             onChange={handleChange}
279 |           />
280 |         </Space>
281 |       </div>
282 |     )
283 |   }
284 | 
285 |   return (
286 |     <Fragment>
287 |       <PaginationContext.Provider
288 |         value={{
289 |           totalPage,
290 |           pageSize,
291 |           changePage: handleChange,
292 |           showPagination,
293 |         }}
294 |       >
295 |         {props.children?.(
296 |           showPagination
297 |             ? dataSource?.slice(startIndex, endIndex + 1)
298 |             : dataSource,
299 |           renderPagination(),
300 |           { startIndex }
301 |         )}
302 |       </PaginationContext.Provider>
303 |     </Fragment>
304 |   )
305 | }
306 | 
307 | const RowComp: ReactFC<React.HTMLAttributes<HTMLTableRowElement>> = (props) => {
308 |   const prefixCls = usePrefixCls('formily-array-table')
309 |   const index = props['data-row-key'] || 0
310 |   return (
311 |     <SortableRow
312 |       lockAxis="y"
313 |       {...props}
314 |       index={index}
315 |       className={cls(props.className, `${prefixCls}-row-${index + 1}`)}
316 |     />
317 |   )
318 | }
319 | 
320 | export const ArrayTable: ComposedArrayTable = observer((props) => {
321 |   const ref = useRef<HTMLDivElement>()
322 |   const field = useField<ArrayField>()
323 |   const prefixCls = usePrefixCls('formily-array-table')
324 |   const dataSource = Array.isArray(field.value) ? field.value.slice() : []
325 |   const sources = useArrayTableSources()
326 |   const columns = useArrayTableColumns(dataSource, field, sources)
327 |   const pagination = isBool(props.pagination)
328 |     ? { showPagination: props.pagination }
329 |     : props.pagination
330 |   const addition = useAddition()
331 |   const { onAdd, onCopy, onRemove, onMoveDown, onMoveUp } = props
332 |   const defaultRowKey = (record: any) => {
333 |     return dataSource.indexOf(record)
334 |   }
335 |   const addTdStyles = (id: number) => {
336 |     const node = ref.current?.querySelector(`.${prefixCls}-row-${id}`)
337 |     const helper = document.body.querySelector(`.${prefixCls}-sort-helper`)
338 |     if (!helper) return
339 |     const tds = node?.querySelectorAll('td')
340 |     if (!tds) return
341 |     requestAnimationFrame(() => {
342 |       helper.querySelectorAll('td').forEach((td, index) => {
343 |         if (tds[index]) {
344 |           td.style.width = getComputedStyle(tds[index]).width
345 |         }
346 |       })
347 |     })
348 |   }
349 |   const getWrapperComp = useCallback(
350 |     (dataSource: any[], start: number) => (props: any) =>
351 |       (
352 |         <SortableBody
353 |           {...props}
354 |           start={start}
355 |           list={dataSource.slice()}
356 |           accessibility={{
357 |             container: ref.current || undefined,
358 |           }}
359 |           onSortStart={(event) => {
360 |             addTdStyles(event.active.id as number)
361 |           }}
362 |           onSortEnd={({ oldIndex, newIndex }) => {
363 |             field.move(oldIndex, newIndex)
364 |           }}
365 |           className={cls(`${prefixCls}-sort-helper`, props.className)}
366 |         />
367 |       ),
368 |     [field]
369 |   )
370 |   return (
371 |     <ArrayTablePagination {...pagination} dataSource={dataSource}>
372 |       {(dataSource, pager, { startIndex }) => (
373 |         <div ref={ref} className={prefixCls}>
374 |           <ArrayBase
375 |             onAdd={onAdd}
376 |             onCopy={onCopy}
377 |             onRemove={onRemove}
378 |             onMoveUp={onMoveUp}
379 |             onMoveDown={onMoveDown}
380 |           >
381 |             <Table
382 |               size="small"
383 |               bordered
384 |               rowKey={defaultRowKey}
385 |               {...props}
386 |               onChange={() => {}}
387 |               pagination={false}
388 |               columns={columns}
389 |               dataSource={dataSource}
390 |               components={{
391 |                 body: {
392 |                   wrapper: getWrapperComp(dataSource, startIndex),
393 |                   row: RowComp,
394 |                 },
395 |               }}
396 |             />
397 |             <div style={{ marginTop: 5, marginBottom: 5 }}>{pager}</div>
398 |             {sources.map((column, key) => {
399 |               //专门用来承接对Column的状态管理
400 |               if (!isColumnComponent(column.schema)) return
401 |               return React.createElement(RecursionField, {
402 |                 name: column.name,
403 |                 schema: column.schema,
404 |                 onlyRenderSelf: true,
405 |                 key,
406 |               })
407 |             })}
408 |             {addition}
409 |           </ArrayBase>
410 |         </div>
411 |       )}
412 |     </ArrayTablePagination>
413 |   )
414 | })
415 | 
416 | ArrayTable.displayName = 'ArrayTable'
417 | 
418 | ArrayTable.Column = () => {
419 |   return <Fragment />
420 | }
421 | 
422 | ArrayBase.mixin(ArrayTable)
423 | 
424 | const Addition: ArrayBaseMixins['Addition'] = (props) => {
425 |   const array = ArrayBase.useArray()
426 |   const {
427 |     totalPage = 0,
428 |     pageSize = 10,
429 |     changePage,
430 |     showPagination,
431 |   } = usePagination()
432 |   return (
433 |     <ArrayBase.Addition
434 |       {...props}
435 |       onClick={(e) => {
436 |         // 如果添加数据后将超过当前页,则自动切换到下一页
437 |         const total = array?.field?.value.length || 0
438 |         if (
439 |           showPagination &&
440 |           total === totalPage * pageSize + 1 &&
441 |           isFn(changePage)
442 |         ) {
443 |           changePage(totalPage + 1)
444 |         }
445 |         props.onClick?.(e)
446 |       }}
447 |     />
448 |   )
449 | }
450 | ArrayTable.Addition = Addition
451 | 
452 | export default ArrayTable
453 | 
```

--------------------------------------------------------------------------------
/packages/next/src/form-item/index.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, { useState, useRef, useEffect } from 'react'
  2 | import cls from 'classnames'
  3 | import {
  4 |   usePrefixCls,
  5 |   pickDataProps,
  6 |   QuestionCircleOutlinedIcon,
  7 |   CloseCircleOutlinedIcon,
  8 |   CheckCircleOutlinedIcon,
  9 |   ExclamationCircleOutlinedIcon,
 10 | } from '../__builtins__'
 11 | import { isVoidField } from '@formily/core'
 12 | import { connect, mapProps } from '@formily/react'
 13 | import { useFormLayout, FormLayoutShallowContext } from '../form-layout'
 14 | import { Balloon } from '@alifd/next'
 15 | 
 16 | export interface IFormItemProps {
 17 |   className?: string
 18 |   style?: React.CSSProperties
 19 |   prefix?: string
 20 |   label?: React.ReactNode
 21 |   colon?: boolean
 22 |   layout?: 'vertical' | 'horizontal' | 'inline'
 23 |   tooltip?: React.ReactNode
 24 |   tooltipLayout?: 'icon' | 'text'
 25 |   tooltipIcon?: React.ReactNode
 26 |   labelFor?: string
 27 |   labelStyle?: React.CSSProperties
 28 |   labelAlign?: 'left' | 'right'
 29 |   labelWrap?: boolean
 30 |   labelWidth?: number | string
 31 |   wrapperWidth?: number | string
 32 |   labelCol?: number
 33 |   wrapperCol?: number
 34 |   wrapperAlign?: 'left' | 'right'
 35 |   wrapperWrap?: boolean
 36 |   wrapperStyle?: React.CSSProperties
 37 |   fullness?: boolean
 38 |   addonBefore?: React.ReactNode
 39 |   addonAfter?: React.ReactNode
 40 |   size?: 'small' | 'default' | 'large'
 41 |   inset?: boolean
 42 |   extra?: React.ReactNode
 43 |   feedbackText?: React.ReactNode
 44 |   feedbackLayout?: 'loose' | 'terse' | 'popover' | 'none' | (string & {})
 45 |   feedbackStatus?: 'error' | 'warning' | 'success' | 'pending' | (string & {})
 46 |   feedbackIcon?: React.ReactNode
 47 |   asterisk?: boolean
 48 |   gridSpan?: number
 49 |   bordered?: boolean
 50 | }
 51 | 
 52 | type ComposeFormItem = React.FC<React.PropsWithChildren<IFormItemProps>> & {
 53 |   BaseItem?: React.FC<React.PropsWithChildren<IFormItemProps>>
 54 | }
 55 | 
 56 | const useFormItemLayout = (props: IFormItemProps) => {
 57 |   const layout = useFormLayout()
 58 |   const layoutType = props.layout ?? layout.layout ?? 'horizontal'
 59 |   return {
 60 |     ...props,
 61 |     layout: layoutType,
 62 |     colon: props.colon ?? layout.colon,
 63 |     labelAlign:
 64 |       layoutType === 'vertical'
 65 |         ? props.labelAlign ?? 'left'
 66 |         : props.labelAlign ?? layout.labelAlign ?? 'right',
 67 |     labelWrap: props.labelWrap ?? layout.labelWrap,
 68 |     labelWidth: props.labelWidth ?? layout.labelWidth,
 69 |     wrapperWidth: props.wrapperWidth ?? layout.wrapperWidth,
 70 |     labelCol: props.labelCol ?? layout.labelCol,
 71 |     wrapperCol: props.wrapperCol ?? layout.wrapperCol,
 72 |     wrapperAlign: props.wrapperAlign ?? layout.wrapperAlign,
 73 |     wrapperWrap: props.wrapperWrap ?? layout.wrapperWrap,
 74 |     fullness: props.fullness ?? layout.fullness,
 75 |     size: props.size ?? layout.size,
 76 |     inset: props.inset ?? layout.inset,
 77 |     asterisk: props.asterisk,
 78 |     bordered: props.bordered ?? layout.bordered,
 79 |     feedbackIcon: props.feedbackIcon,
 80 |     feedbackLayout: props.feedbackLayout ?? layout.feedbackLayout ?? 'loose',
 81 |     tooltipLayout: props.tooltipLayout ?? layout.tooltipLayout ?? 'icon',
 82 |     tooltipIcon: props.tooltipIcon ?? layout.tooltipIcon ?? (
 83 |       <QuestionCircleOutlinedIcon />
 84 |     ),
 85 |   }
 86 | }
 87 | 
 88 | function useOverflow<
 89 |   Container extends HTMLElement,
 90 |   Content extends HTMLElement
 91 | >() {
 92 |   const [overflow, setOverflow] = useState(false)
 93 |   const containerRef = useRef<Container>()
 94 |   const contentRef = useRef<Content>()
 95 |   const layout = useFormLayout()
 96 |   const labelCol = JSON.stringify(layout.labelCol)
 97 | 
 98 |   useEffect(() => {
 99 |     requestAnimationFrame(() => {
100 |       if (containerRef.current && contentRef.current) {
101 |         const contentWidth = contentRef.current.getBoundingClientRect().width
102 |         const containerWidth =
103 |           containerRef.current.getBoundingClientRect().width
104 |         if (contentWidth && containerWidth && containerWidth < contentWidth) {
105 |           if (!overflow) setOverflow(true)
106 |         } else {
107 |           if (overflow) setOverflow(false)
108 |         }
109 |       }
110 |     })
111 |   }, [labelCol])
112 | 
113 |   return {
114 |     overflow,
115 |     containerRef,
116 |     contentRef,
117 |   }
118 | }
119 | 
120 | const ICON_MAP = {
121 |   error: <CloseCircleOutlinedIcon />,
122 |   success: <CheckCircleOutlinedIcon />,
123 |   warning: <ExclamationCircleOutlinedIcon />,
124 | }
125 | 
126 | export const BaseItem: React.FC<React.PropsWithChildren<IFormItemProps>> = (
127 |   props
128 | ) => {
129 |   const { children, ...others } = props
130 |   const [active, setActive] = useState(false)
131 |   const formLayout = useFormItemLayout(others)
132 |   const { containerRef, contentRef, overflow } = useOverflow<
133 |     HTMLDivElement,
134 |     HTMLSpanElement
135 |   >()
136 |   const {
137 |     label,
138 |     style,
139 |     layout,
140 |     colon = true,
141 |     addonBefore,
142 |     addonAfter,
143 |     asterisk,
144 |     feedbackStatus,
145 |     extra,
146 |     feedbackText,
147 |     fullness = true,
148 |     feedbackLayout,
149 |     feedbackIcon,
150 |     inset,
151 |     bordered = true,
152 |     labelWidth,
153 |     wrapperWidth,
154 |     labelCol,
155 |     wrapperCol,
156 |     labelAlign,
157 |     wrapperAlign = 'left',
158 |     size,
159 |     labelWrap,
160 |     wrapperWrap,
161 |     tooltip,
162 |     tooltipLayout,
163 |     tooltipIcon,
164 |   } = formLayout
165 |   const labelStyle = { ...formLayout.labelStyle }
166 |   const wrapperStyle = { ...formLayout.wrapperStyle }
167 |   // 固定宽度
168 |   let enableCol = false
169 |   if (labelWidth || wrapperWidth) {
170 |     if (labelWidth) {
171 |       labelStyle.width = labelWidth === 'auto' ? undefined : labelWidth
172 |       labelStyle.maxWidth = labelWidth === 'auto' ? undefined : labelWidth
173 |     }
174 |     if (wrapperWidth) {
175 |       wrapperStyle.width = wrapperWidth === 'auto' ? undefined : wrapperWidth
176 |       wrapperStyle.maxWidth = wrapperWidth === 'auto' ? undefined : wrapperWidth
177 |     }
178 |     // 栅格模式
179 |   }
180 |   if (labelCol || wrapperCol) {
181 |     if (!labelStyle.width && !wrapperStyle.width && layout !== 'vertical') {
182 |       enableCol = true
183 |     }
184 |   }
185 |   const prefixCls = usePrefixCls('formily-item', props)
186 |   const prefix = usePrefixCls()
187 |   const formatChildren =
188 |     feedbackLayout === 'popover' ? (
189 |       <Balloon
190 |         needAdjust
191 |         align="t"
192 |         closable={false}
193 |         trigger={children}
194 |         visible={!!feedbackText}
195 |       >
196 |         <div
197 |           className={cls({
198 |             [`${prefixCls}-${feedbackStatus}-help`]: !!feedbackStatus,
199 |             [`${prefixCls}-help`]: true,
200 |           })}
201 |         >
202 |           {ICON_MAP[feedbackStatus]} {feedbackText}
203 |         </div>
204 |       </Balloon>
205 |     ) : (
206 |       children
207 |     )
208 | 
209 |   const gridStyles: React.CSSProperties = {}
210 | 
211 |   const getOverflowTooltip = () => {
212 |     if (overflow) {
213 |       return (
214 |         <div>
215 |           <div>{label}</div>
216 |           <div>{tooltip}</div>
217 |         </div>
218 |       )
219 |     }
220 |     return tooltip
221 |   }
222 | 
223 |   const renderLabelText = () => {
224 |     const labelChildren = (
225 |       <div className={cls(`${prefixCls}-label-content`)} ref={containerRef}>
226 |         <span ref={contentRef}>
227 |           {asterisk && (
228 |             <span className={cls(`${prefixCls}-asterisk`)}>{'*'}</span>
229 |           )}
230 |           <label htmlFor={props.labelFor}>{label}</label>
231 |         </span>
232 |       </div>
233 |     )
234 | 
235 |     if ((tooltipLayout === 'text' && tooltip) || overflow) {
236 |       return (
237 |         <Balloon.Tooltip align="t" trigger={labelChildren}>
238 |           {getOverflowTooltip()}
239 |         </Balloon.Tooltip>
240 |       )
241 |     }
242 |     return labelChildren
243 |   }
244 | 
245 |   const renderTooltipIcon = () => {
246 |     if (tooltip && tooltipLayout === 'icon' && !overflow) {
247 |       return (
248 |         <span className={cls(`${prefixCls}-label-tooltip-icon`)}>
249 |           <Balloon.Tooltip align="t" trigger={tooltipIcon}>
250 |             {tooltip}
251 |           </Balloon.Tooltip>
252 |         </span>
253 |       )
254 |     }
255 |   }
256 | 
257 |   const renderLabel = () => {
258 |     if (!label) return null
259 |     return (
260 |       <div
261 |         className={cls({
262 |           [`${prefixCls}-label`]: true,
263 |           [`${prefixCls}-label-tooltip`]:
264 |             (tooltip && tooltipLayout === 'text') || overflow,
265 |           [`${prefixCls}-item-col-${labelCol}`]: enableCol && !!labelCol,
266 |         })}
267 |         style={labelStyle}
268 |       >
269 |         {renderLabelText()}
270 |         {renderTooltipIcon()}
271 |         {label !== ' ' && (
272 |           <span className={cls(`${prefixCls}-colon`)}>{colon ? ':' : ''}</span>
273 |         )}
274 |       </div>
275 |     )
276 |   }
277 | 
278 |   return (
279 |     <div
280 |       {...pickDataProps(props)}
281 |       style={{
282 |         ...style,
283 |         ...gridStyles,
284 |       }}
285 |       data-grid-span={props.gridSpan}
286 |       className={cls({
287 |         [`${prefixCls}`]: true,
288 |         [`${prefixCls}-layout-${layout}`]: true,
289 |         [`${prefixCls}-${feedbackStatus}`]: !!feedbackStatus,
290 |         [`${prefixCls}-feedback-has-text`]: !!feedbackText,
291 |         [`${prefixCls}-size-${size}`]: !!size,
292 |         [`${prefixCls}-feedback-layout-${feedbackLayout}`]: !!feedbackLayout,
293 |         [`${prefixCls}-fullness`]: !!fullness || !!inset || !!feedbackIcon,
294 |         [`${prefixCls}-inset`]: !!inset,
295 |         [`${prefix}input`]: !!inset,
296 |         [`${prefixCls}-active`]: active,
297 |         [`${prefix}focus`]: active,
298 |         [`${prefixCls}-inset-active`]: !!inset && active,
299 |         [`${prefixCls}-label-align-${labelAlign}`]: true,
300 |         [`${prefixCls}-control-align-${wrapperAlign}`]: true,
301 |         [`${prefixCls}-label-wrap`]: !!labelWrap,
302 |         [`${prefixCls}-control-wrap`]: !!wrapperWrap,
303 |         [`${prefixCls}-bordered-none`]: bordered === false,
304 |         [props.className]: !!props.className,
305 |       })}
306 |       onFocus={() => {
307 |         if (feedbackIcon || inset) {
308 |           setActive(true)
309 |         }
310 |       }}
311 |       onBlur={() => {
312 |         if (feedbackIcon || inset) {
313 |           setActive(false)
314 |         }
315 |       }}
316 |     >
317 |       {renderLabel()}
318 |       <div
319 |         className={cls({
320 |           [`${prefixCls}-control`]: true,
321 |           [`${prefixCls}-item-col-${wrapperCol}`]:
322 |             enableCol && !!wrapperCol && label,
323 |         })}
324 |       >
325 |         <div className={cls(`${prefixCls}-control-content`)}>
326 |           {addonBefore && (
327 |             <div className={cls(`${prefixCls}-addon-before`)}>
328 |               {addonBefore}
329 |             </div>
330 |           )}
331 |           <div
332 |             style={wrapperStyle}
333 |             className={cls({
334 |               [`${prefixCls}-control-content-component`]: true,
335 |               [`${prefixCls}-control-content-component-has-feedback-icon`]:
336 |                 !!feedbackIcon,
337 |               [`${prefix}input`]: !!feedbackIcon,
338 |               [`${prefixCls}-active`]: active,
339 |               [`${prefix}focus`]: active,
340 |             })}
341 |           >
342 |             <FormLayoutShallowContext.Provider value={{ size }}>
343 |               {formatChildren}
344 |             </FormLayoutShallowContext.Provider>
345 |             {feedbackIcon && (
346 |               <div className={cls(`${prefixCls}-feedback-icon`)}>
347 |                 {feedbackIcon}
348 |               </div>
349 |             )}
350 |           </div>
351 |           {addonAfter && (
352 |             <div className={cls(`${prefixCls}-addon-after`)}>{addonAfter}</div>
353 |           )}
354 |         </div>
355 |         {!!feedbackText &&
356 |           feedbackLayout !== 'popover' &&
357 |           feedbackLayout !== 'none' && (
358 |             <div
359 |               className={cls({
360 |                 [`${prefixCls}-${feedbackStatus}-help`]: !!feedbackStatus,
361 |                 [`${prefixCls}-help`]: true,
362 |                 [`${prefixCls}-help-enter`]: true,
363 |                 [`${prefixCls}-help-enter-active`]: true,
364 |               })}
365 |             >
366 |               {feedbackText}
367 |             </div>
368 |           )}
369 |         {extra && <div className={cls(`${prefixCls}-extra`)}>{extra}</div>}
370 |       </div>
371 |     </div>
372 |   )
373 | }
374 | 
375 | // 适配
376 | export const FormItem: ComposeFormItem = connect(
377 |   BaseItem,
378 |   mapProps((props, field) => {
379 |     if (isVoidField(field))
380 |       return {
381 |         label: field.title || props.label,
382 |         asterisk: props.asterisk,
383 |         extra: props.extra || field.description,
384 |       }
385 |     if (!field) return props
386 |     const takeFeedbackStatus = () => {
387 |       if (field.validating) return 'pending'
388 |       return field.decoratorProps.feedbackStatus || field.validateStatus
389 |     }
390 |     const takeMessage = () => {
391 |       const split = (messages: any[]) => {
392 |         return messages.reduce((buf, text, index) => {
393 |           if (!text) return buf
394 |           return index < messages.length - 1
395 |             ? buf.concat([text, ', '])
396 |             : buf.concat([text])
397 |         }, [])
398 |       }
399 |       if (field.validating) return
400 |       if (props.feedbackText) return props.feedbackText
401 |       if (field.selfErrors.length) return split(field.selfErrors)
402 |       if (field.selfWarnings.length) return split(field.selfWarnings)
403 |       if (field.selfSuccesses.length) return split(field.selfSuccesses)
404 |     }
405 |     const takeAsterisk = () => {
406 |       if (field.required && field.pattern !== 'readPretty') {
407 |         return true
408 |       }
409 |       if ('asterisk' in props) {
410 |         return props.asterisk
411 |       }
412 |       return false
413 |     }
414 |     return {
415 |       label: props.label || field.title,
416 |       feedbackStatus: takeFeedbackStatus(),
417 |       feedbackText: takeMessage(),
418 |       asterisk: takeAsterisk(),
419 |       extra: props.extra || field.description,
420 |     }
421 |   })
422 | )
423 | 
424 | FormItem.BaseItem = BaseItem
425 | 
426 | export default FormItem
427 | 
```
Page 28/52FirstPrevNextLast