#
tokens: 49196/50000 8/1152 files (page 27/52)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 27 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/array-collapse/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { ArrayField } from '@formily/core'
  2 | import { ISchema } from '@formily/json-schema'
  3 | import { observer } from '@formily/reactive-vue'
  4 | import {
  5 |   Fragment,
  6 |   h,
  7 |   RecursionField,
  8 |   useField,
  9 |   useFieldSchema,
 10 | } from '@formily/vue'
 11 | import type {
 12 |   Collapse as CollapseProps,
 13 |   CollapseItem as CollapseItemProps,
 14 | } from 'element-ui'
 15 | import { Badge, Card, Collapse, CollapseItem, Empty, Row } from 'element-ui'
 16 | import { defineComponent, ref, Ref, watchEffect } from 'vue-demi'
 17 | import { ArrayBase } from '../array-base'
 18 | import { stylePrefix } from '../__builtins__/configs'
 19 | import { composeExport } from '../__builtins__/shared'
 20 | 
 21 | export interface IArrayCollapseProps extends CollapseProps {
 22 |   defaultOpenPanelCount?: number
 23 | }
 24 | 
 25 | const isAdditionComponent = (schema: ISchema) => {
 26 |   return schema['x-component']?.indexOf?.('Addition') > -1
 27 | }
 28 | 
 29 | const isIndexComponent = (schema: ISchema) => {
 30 |   return schema['x-component']?.indexOf?.('Index') > -1
 31 | }
 32 | 
 33 | const isRemoveComponent = (schema: ISchema) => {
 34 |   return schema['x-component']?.indexOf?.('Remove') > -1
 35 | }
 36 | 
 37 | const isMoveUpComponent = (schema: ISchema) => {
 38 |   return schema['x-component']?.indexOf?.('MoveUp') > -1
 39 | }
 40 | 
 41 | const isMoveDownComponent = (schema: ISchema) => {
 42 |   return schema['x-component']?.indexOf?.('MoveDown') > -1
 43 | }
 44 | 
 45 | const isOperationComponent = (schema: ISchema) => {
 46 |   return (
 47 |     isAdditionComponent(schema) ||
 48 |     isRemoveComponent(schema) ||
 49 |     isMoveDownComponent(schema) ||
 50 |     isMoveUpComponent(schema)
 51 |   )
 52 | }
 53 | 
 54 | const range = (count: number) => Array.from({ length: count }).map((_, i) => i)
 55 | 
 56 | const takeDefaultActiveKeys = (
 57 |   dataSourceLength: number,
 58 |   defaultOpenPanelCount: number,
 59 |   accordion = false
 60 | ) => {
 61 |   if (accordion) {
 62 |     return 0
 63 |   }
 64 |   if (dataSourceLength < defaultOpenPanelCount) return range(dataSourceLength)
 65 | 
 66 |   return range(defaultOpenPanelCount)
 67 | }
 68 | 
 69 | const insertActiveKeys = (
 70 |   activeKeys: number[] | number,
 71 |   index: number,
 72 |   accordion = false
 73 | ) => {
 74 |   if (accordion) return index
 75 |   if ((activeKeys as number[]).length <= index)
 76 |     return (activeKeys as number[]).concat(index)
 77 |   return (activeKeys as number[]).reduce((buf, key) => {
 78 |     if (key < index) return buf.concat(key)
 79 |     if (key === index) return buf.concat([key, key + 1])
 80 |     return buf.concat(key + 1)
 81 |   }, [])
 82 | }
 83 | 
 84 | export const ArrayCollapseInner = observer(
 85 |   defineComponent<IArrayCollapseProps>({
 86 |     name: 'FArrayCollapse',
 87 |     props: {
 88 |       defaultOpenPanelCount: {
 89 |         type: Number,
 90 |         default: 5,
 91 |       },
 92 |     },
 93 |     setup(props, { attrs }) {
 94 |       const fieldRef = useField<ArrayField>()
 95 |       const schemaRef = useFieldSchema()
 96 | 
 97 |       const prefixCls = `${stylePrefix}-array-collapse`
 98 |       const activeKeys: Ref<number[] | number> = ref([])
 99 | 
100 |       watchEffect(() => {
101 |         const field = fieldRef.value
102 |         const dataSource = Array.isArray(field.value) ? field.value.slice() : []
103 |         if (!field.modified && dataSource.length) {
104 |           activeKeys.value = takeDefaultActiveKeys(
105 |             dataSource.length,
106 |             props.defaultOpenPanelCount,
107 |             attrs.accordion as boolean
108 |           )
109 |         }
110 |       })
111 | 
112 |       const { getKey, keyMap } = ArrayBase.useKey(schemaRef.value)
113 | 
114 |       return () => {
115 |         const field = fieldRef.value
116 |         const schema = schemaRef.value
117 |         const dataSource = Array.isArray(field.value) ? field.value.slice() : []
118 |         if (!schema) throw new Error('can not found schema object')
119 | 
120 |         const renderItems = () => {
121 |           if (!dataSource.length) {
122 |             return null
123 |           }
124 | 
125 |           const items = dataSource?.map((item, index) => {
126 |             const items = Array.isArray(schema.items)
127 |               ? schema.items[index] || schema.items[0]
128 |               : schema.items
129 |             const key = getKey(item, index)
130 |             const panelProps = field
131 |               .query(`${field.address}.${index}`)
132 |               .get('componentProps')
133 |             const props: CollapseItemProps = items['x-component-props']
134 |             const headerTitle = panelProps?.title || props.title || field.title
135 |             const path = field.address.concat(index)
136 |             const errors = field.form.queryFeedbacks({
137 |               type: 'error',
138 |               address: `${path}.**`,
139 |             })
140 | 
141 |             const title = h(
142 |               ArrayBase.Item,
143 |               {
144 |                 props: {
145 |                   index,
146 |                   record: item,
147 |                 },
148 |               },
149 |               {
150 |                 default: () => [
151 |                   h(
152 |                     RecursionField,
153 |                     {
154 |                       props: {
155 |                         schema: items,
156 |                         name: index,
157 |                         filterProperties: (schema) => {
158 |                           if (!isIndexComponent(schema)) return false
159 |                           return true
160 |                         },
161 |                         onlyRenderProperties: true,
162 |                       },
163 |                     },
164 |                     {}
165 |                   ),
166 |                   errors.length
167 |                     ? h(
168 |                         Badge,
169 |                         {
170 |                           class: [`${prefixCls}-errors-badge`],
171 |                           props: {
172 |                             value: errors.length,
173 |                           },
174 |                         },
175 |                         { default: () => headerTitle }
176 |                       )
177 |                     : headerTitle,
178 |                 ],
179 |               }
180 |             )
181 |             const extra = h(
182 |               ArrayBase.Item,
183 |               {
184 |                 props: {
185 |                   index,
186 |                   record: item,
187 |                 },
188 |               },
189 |               {
190 |                 default: () => [
191 |                   h(
192 |                     RecursionField,
193 |                     {
194 |                       props: {
195 |                         schema: items,
196 |                         name: index,
197 |                         filterProperties: (schema) => {
198 |                           if (!isOperationComponent(schema)) return false
199 |                           return true
200 |                         },
201 |                         onlyRenderProperties: true,
202 |                       },
203 |                     },
204 |                     {}
205 |                   ),
206 |                 ],
207 |               }
208 |             )
209 |             const content = h(
210 |               RecursionField,
211 |               {
212 |                 props: {
213 |                   schema: items,
214 |                   name: index,
215 |                   filterProperties: (schema) => {
216 |                     if (isIndexComponent(schema)) return false
217 |                     if (isOperationComponent(schema)) return false
218 |                     return true
219 |                   },
220 |                 },
221 |               },
222 |               {}
223 |             )
224 | 
225 |             return h(
226 |               CollapseItem,
227 |               {
228 |                 attrs: {
229 |                   ...props,
230 |                   ...panelProps,
231 |                   name: index,
232 |                 },
233 |                 key,
234 |               },
235 |               {
236 |                 default: () => [
237 |                   h(
238 |                     ArrayBase.Item,
239 |                     {
240 |                       props: {
241 |                         index,
242 |                         record: item,
243 |                       },
244 |                     },
245 |                     {
246 |                       default: () => [content],
247 |                     }
248 |                   ),
249 |                 ],
250 |                 title: () =>
251 |                   h(
252 |                     Row,
253 |                     {
254 |                       style: { flex: 1 },
255 |                       props: {
256 |                         type: 'flex',
257 |                         justify: 'space-between',
258 |                       },
259 |                     },
260 |                     {
261 |                       default: () => [
262 |                         h('span', {}, { default: () => title }),
263 |                         h('span', {}, { default: () => extra }),
264 |                       ],
265 |                     }
266 |                   ),
267 |               }
268 |             )
269 |           })
270 | 
271 |           return h(
272 |             Collapse,
273 |             {
274 |               class: [`${prefixCls}-item`],
275 |               attrs: {
276 |                 ...attrs,
277 |                 value: activeKeys.value,
278 |               },
279 |               on: {
280 |                 change: (keys: number[] | number) => {
281 |                   activeKeys.value = keys
282 |                 },
283 |               },
284 |             },
285 |             {
286 |               default: () => [items],
287 |             }
288 |           )
289 |         }
290 |         const renderAddition = () => {
291 |           return schema.reduceProperties((addition, schema) => {
292 |             if (isAdditionComponent(schema)) {
293 |               return h(
294 |                 RecursionField,
295 |                 {
296 |                   props: {
297 |                     schema,
298 |                     name: 'addition',
299 |                   },
300 |                 },
301 |                 {}
302 |               )
303 |             }
304 |             return addition
305 |           }, null)
306 |         }
307 |         const renderEmpty = () => {
308 |           if (dataSource?.length) return
309 |           return h(
310 |             Card,
311 |             {
312 |               class: [`${prefixCls}-item`],
313 |               attrs: {
314 |                 shadow: 'never',
315 |                 ...attrs,
316 |                 header: attrs.title || field.title,
317 |               },
318 |             },
319 |             {
320 |               default: () =>
321 |                 h(
322 |                   Empty,
323 |                   { props: { description: 'No Data', imageSize: 100 } },
324 |                   {}
325 |                 ),
326 |             }
327 |           )
328 |         }
329 | 
330 |         return h(
331 |           'div',
332 |           {
333 |             class: [prefixCls],
334 |           },
335 |           {
336 |             default: () =>
337 |               h(
338 |                 ArrayBase,
339 |                 {
340 |                   props: {
341 |                     keyMap,
342 |                   },
343 |                   on: {
344 |                     add: (index: number) => {
345 |                       activeKeys.value = insertActiveKeys(
346 |                         activeKeys.value,
347 |                         index,
348 |                         attrs.accordion as boolean
349 |                       )
350 |                     },
351 |                   },
352 |                 },
353 |                 {
354 |                   default: () => [
355 |                     renderEmpty(),
356 |                     renderItems(),
357 |                     renderAddition(),
358 |                   ],
359 |                 }
360 |               ),
361 |           }
362 |         )
363 |       }
364 |     },
365 |   })
366 | )
367 | 
368 | export const ArrayCollapseItem = defineComponent<CollapseItemProps>({
369 |   name: 'FArrayCollapseItem',
370 |   setup(_props, { slots }) {
371 |     return () => h(Fragment, {}, slots)
372 |   },
373 | })
374 | 
375 | export const ArrayCollapse = composeExport(ArrayCollapseInner, {
376 |   Item: ArrayCollapseItem,
377 |   Index: ArrayBase.Index,
378 |   SortHandle: ArrayBase.SortHandle,
379 |   Addition: ArrayBase.Addition,
380 |   Remove: ArrayBase.Remove,
381 |   MoveDown: ArrayBase.MoveDown,
382 |   MoveUp: ArrayBase.MoveUp,
383 |   useArray: ArrayBase.useArray,
384 |   useIndex: ArrayBase.useIndex,
385 |   useRecord: ArrayBase.useRecord,
386 | })
387 | 
388 | export default ArrayCollapse
389 | 
```

--------------------------------------------------------------------------------
/packages/next/src/select-table/index.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import React, { useState, useMemo } from 'react'
  2 | import {
  3 |   observer,
  4 |   useFieldSchema,
  5 |   useField,
  6 |   Schema,
  7 |   RecursionField,
  8 | } from '@formily/react'
  9 | import cls from 'classnames'
 10 | import { GeneralField, FieldDisplayTypes } from '@formily/core'
 11 | import { isArr, isBool, isFn } from '@formily/shared'
 12 | import { Search, Table } from '@alifd/next'
 13 | import { TableProps, ColumnProps } from '@alifd/next/types/table'
 14 | import { SearchProps } from '@alifd/next/types/search'
 15 | import { useFilterOptions } from './useFilterOptions'
 16 | import { useFlatOptions } from './useFlatOptions'
 17 | import { useSize } from './useSize'
 18 | import { useTitleAddon } from './useTitleAddon'
 19 | import { useCheckSlackly, getIndeterminate } from './useCheckSlackly'
 20 | import { getUISelected, getOutputData } from './utils'
 21 | import { usePrefixCls } from '../__builtins__'
 22 | 
 23 | interface ObservableColumnSource {
 24 |   field: GeneralField
 25 |   columnProps: ColumnProps
 26 |   schema: Schema
 27 |   display: FieldDisplayTypes
 28 |   name: string
 29 | }
 30 | 
 31 | type IFilterOption = boolean | ((option: any, keyword: string) => boolean)
 32 | 
 33 | type IFilterSort = (optionA: any, optionB: any) => number
 34 | 
 35 | export interface ISelectTableColumnProps extends ColumnProps {
 36 |   key: React.ReactText
 37 | }
 38 | 
 39 | export interface ISelectTableProps
 40 |   extends Omit<TableProps, 'primaryKey' | 'onChange'> {
 41 |   mode?: 'multiple' | 'single'
 42 |   dataSource?: any[]
 43 |   optionAsValue?: boolean
 44 |   valueType?: 'all' | 'parent' | 'child' | 'path'
 45 |   showSearch?: boolean
 46 |   searchProps?: SearchProps
 47 |   primaryKey?: string | ((record: any) => string)
 48 |   filterOption?: IFilterOption
 49 |   filterSort?: IFilterSort
 50 |   onSearch?: (keyword: string) => void
 51 |   onChange?: (value: any, options: any) => void
 52 |   value?: any
 53 |   rowSelection?: TableProps['rowSelection'] & {
 54 |     checkStrictly?: boolean
 55 |   }
 56 | }
 57 | 
 58 | type ComposedSelectTable = React.FC<
 59 |   React.PropsWithChildren<ISelectTableProps>
 60 | > & {
 61 |   Column?: React.FC<React.PropsWithChildren<ISelectTableColumnProps>>
 62 | }
 63 | 
 64 | const isColumnComponent = (schema: Schema) => {
 65 |   return schema['x-component']?.indexOf('Column') > -1
 66 | }
 67 | 
 68 | const useSources = () => {
 69 |   const arrayField = useField()
 70 |   const schema = useFieldSchema()
 71 |   const parseSources = (schema: Schema): ObservableColumnSource[] => {
 72 |     if (isColumnComponent(schema)) {
 73 |       if (!schema['x-component-props']?.['dataIndex'] && !schema['name'])
 74 |         return []
 75 |       const name = schema['x-component-props']?.['dataIndex'] || schema['name']
 76 |       const field = arrayField.query(arrayField.address.concat(name)).take()
 77 |       const columnProps =
 78 |         field?.component?.[1] || schema['x-component-props'] || {}
 79 |       const display = field?.display || schema['x-display']
 80 |       return [
 81 |         {
 82 |           name,
 83 |           display,
 84 |           field,
 85 |           schema,
 86 |           columnProps: {
 87 |             title: field?.title || columnProps.title,
 88 |             ...columnProps,
 89 |           },
 90 |         },
 91 |       ]
 92 |     } else if (schema.properties) {
 93 |       return schema.reduceProperties((buf, schema) => {
 94 |         return buf.concat(parseSources(schema))
 95 |       }, [])
 96 |     }
 97 |   }
 98 | 
 99 |   const parseArrayItems = (schema: Schema['items']) => {
100 |     if (!schema) return []
101 |     const sources: ObservableColumnSource[] = []
102 |     const items = isArr(schema) ? schema : [schema]
103 |     return items.reduce((columns, schema) => {
104 |       const item = parseSources(schema)
105 |       if (item) {
106 |         return columns.concat(item)
107 |       }
108 |       return columns
109 |     }, sources)
110 |   }
111 | 
112 |   const validSchema = (
113 |     schema?.type === 'array' && schema?.items ? schema.items : schema
114 |   ) as Schema
115 | 
116 |   return parseArrayItems(validSchema)
117 | }
118 | 
119 | const useColumns = (
120 |   sources: ObservableColumnSource[]
121 | ): TableProps['columns'] => {
122 |   return sources.reduce((buf, { name, columnProps, schema, display }, key) => {
123 |     if (display !== 'visible') return buf
124 |     if (!isColumnComponent(schema)) return buf
125 |     return buf.concat({
126 |       ...columnProps,
127 |       key,
128 |       dataIndex: name,
129 |     })
130 |   }, [])
131 | }
132 | 
133 | const addPrimaryKey = (dataSource, rowKey, primaryKey) =>
134 |   dataSource.map((item) => {
135 |     const children = isArr(item.children)
136 |       ? addPrimaryKey(item.children, rowKey, primaryKey)
137 |       : {}
138 |     return {
139 |       ...item,
140 |       ...children,
141 |       [primaryKey]: rowKey(item),
142 |     }
143 |   })
144 | 
145 | export const SelectTable: ComposedSelectTable = observer((props) => {
146 |   const {
147 |     mode = 'multiple',
148 |     dataSource: propsDataSource,
149 |     optionAsValue,
150 |     valueType = 'all',
151 |     showSearch = false,
152 |     filterOption,
153 |     filterSort,
154 |     onSearch,
155 |     searchProps,
156 |     className,
157 |     value,
158 |     onChange,
159 |     rowSelection,
160 |     primaryKey: rowKey = 'key',
161 |     ...otherTableProps
162 |   } = props
163 |   const prefixCls = usePrefixCls('formily-select-table', props)
164 |   const [searchValue, setSearchValue] = useState<string>()
165 |   const field = useField() as any
166 |   const loading = isBool(props.loading) ? props.loading : field.loading
167 |   const disabled = field.disabled
168 |   const readOnly = field.readOnly
169 |   const readPretty = field.readPretty
170 |   const { searchSize, tableSize } = useSize(
171 |     field.decoratorProps?.size,
172 |     searchProps?.size,
173 |     props?.size
174 |   )
175 |   const primaryKey = isFn(rowKey) ? '__formily_key__' : rowKey
176 |   const sources = useSources()
177 |   const columns = useColumns(sources)
178 | 
179 |   // dataSource
180 |   let dataSource = isArr(propsDataSource) ? propsDataSource : field.dataSource
181 |   dataSource = isFn(rowKey)
182 |     ? addPrimaryKey(dataSource, rowKey, primaryKey)
183 |     : dataSource
184 | 
185 |   // Filter dataSource By Search
186 |   const filteredDataSource = useFilterOptions(
187 |     dataSource,
188 |     searchValue,
189 |     filterOption,
190 |     rowSelection?.checkStrictly
191 |   )
192 | 
193 |   // Order dataSource By filterSort
194 |   const orderedFilteredDataSource = useMemo(() => {
195 |     if (!filterSort) {
196 |       return filteredDataSource
197 |     }
198 |     return [...filteredDataSource].sort((a, b) => filterSort(a, b))
199 |   }, [filteredDataSource, filterSort])
200 | 
201 |   const flatDataSource = useFlatOptions(dataSource)
202 |   const flatFilteredDataSource = useFlatOptions(filteredDataSource)
203 | 
204 |   // 分页或异步查询时,dataSource会丢失已选数据,配置optionAsValue则无法获取已选数据,需要进行合并
205 |   const getWholeDataSource = () => {
206 |     if (optionAsValue && mode === 'multiple' && value?.length) {
207 |       const map = new Map()
208 |       const arr = [...flatDataSource, ...value]
209 |       arr.forEach((item) => {
210 |         if (!map.has(item[primaryKey])) {
211 |           map.set(item[primaryKey], item)
212 |         }
213 |       })
214 |       return [...map.values()]
215 |     }
216 |     return flatDataSource
217 |   }
218 | 
219 |   // selected keys for Table UI
220 |   const selected = getUISelected(
221 |     value,
222 |     flatDataSource,
223 |     primaryKey,
224 |     valueType,
225 |     optionAsValue,
226 |     mode,
227 |     rowSelection?.checkStrictly,
228 |     rowKey
229 |   )
230 | 
231 |   // readPretty Value
232 |   const readPrettyDataSource = useFilterOptions(
233 |     orderedFilteredDataSource,
234 |     selected,
235 |     (value, item) => value.includes(item[primaryKey])
236 |   )
237 | 
238 |   const onInnerSearch = (searchText) => {
239 |     const formatted = (searchText || '').trim()
240 |     setSearchValue(searchText)
241 |     onSearch?.(formatted)
242 |   }
243 | 
244 |   const onInnerChange = (selectedRowKeys: any[]) => {
245 |     if (readOnly) {
246 |       return
247 |     }
248 |     // 筛选后onChange默认的records数据不完整,此处需使用完整数据过滤
249 |     const wholeRecords = getWholeDataSource().filter((item) =>
250 |       selectedRowKeys.includes(item?.[primaryKey])
251 |     )
252 | 
253 |     const { outputValue, outputOptions } = getOutputData(
254 |       selectedRowKeys,
255 |       wholeRecords,
256 |       dataSource,
257 |       primaryKey,
258 |       valueType,
259 |       optionAsValue,
260 |       mode,
261 |       rowSelection?.checkStrictly
262 |     )
263 | 
264 |     onChange?.(outputValue, outputOptions)
265 |   }
266 | 
267 |   const onRowClick = (record) => {
268 |     if (readPretty || disabled || readOnly || record?.disabled) {
269 |       return
270 |     }
271 |     const selectedRowKey = record?.[primaryKey]
272 |     const isSelected = selected?.includes(selectedRowKey)
273 |     let selectedRowKeys = []
274 |     if (mode === 'single') {
275 |       selectedRowKeys = [selectedRowKey]
276 |     } else {
277 |       if (isSelected) {
278 |         selectedRowKeys = selected.filter((item) => item !== selectedRowKey)
279 |       } else {
280 |         selectedRowKeys = [...selected, selectedRowKey]
281 |       }
282 |     }
283 |     if (rowSelection?.checkStrictly !== false) {
284 |       onInnerChange(selectedRowKeys)
285 |     } else {
286 |       onSlacklyChange(selectedRowKeys)
287 |     }
288 |   }
289 | 
290 |   // TreeData SlacklyChange
291 |   const onSlacklyChange = (currentSelected: any[]) => {
292 |     let { selectedRowKeys } = useCheckSlackly(
293 |       currentSelected,
294 |       selected,
295 |       flatDataSource,
296 |       flatFilteredDataSource,
297 |       primaryKey,
298 |       rowSelection?.checkStrictly
299 |     )
300 |     onInnerChange(selectedRowKeys)
301 |   }
302 | 
303 |   // Table All Checkbox
304 |   const titleAddon = useTitleAddon(
305 |     selected,
306 |     flatDataSource,
307 |     flatFilteredDataSource,
308 |     primaryKey,
309 |     mode,
310 |     disabled,
311 |     readOnly,
312 |     rowSelection?.checkStrictly,
313 |     onInnerChange
314 |   )
315 | 
316 |   return (
317 |     <div className={prefixCls}>
318 |       {showSearch ? (
319 |         <Search
320 |           {...searchProps}
321 |           className={cls(`${prefixCls}-search`, searchProps?.className)}
322 |           style={{ width: '100%', ...searchProps?.style }}
323 |           onSearch={onInnerSearch}
324 |           onChange={onInnerSearch}
325 |           disabled={disabled}
326 |           readOnly={readOnly}
327 |           size={searchSize}
328 |           buttonProps={{ ...searchProps?.buttonProps, loading }} // fusion
329 |         />
330 |       ) : null}
331 |       <Table
332 |         {...otherTableProps}
333 |         className={cls(`${prefixCls}-table`, className)}
334 |         dataSource={
335 |           readPretty ? readPrettyDataSource : orderedFilteredDataSource
336 |         }
337 |         rowSelection={
338 |           readPretty
339 |             ? undefined
340 |             : {
341 |                 ...rowSelection,
342 |                 ...titleAddon,
343 |                 getProps: (record, index) => ({
344 |                   ...(rowSelection?.getProps?.(record, index) as any),
345 |                   ...(rowSelection?.checkStrictly !== false
346 |                     ? {}
347 |                     : {
348 |                         indeterminate: getIndeterminate(
349 |                           record,
350 |                           flatDataSource,
351 |                           selected,
352 |                           primaryKey
353 |                         ),
354 |                       }), // 父子关联模式indeterminate值
355 |                   disabled: disabled || record?.disabled,
356 |                 }), // fusion
357 |                 selectedRowKeys: selected,
358 |                 onChange:
359 |                   rowSelection?.checkStrictly !== false
360 |                     ? onInnerChange
361 |                     : onSlacklyChange,
362 |                 mode,
363 |               }
364 |         }
365 |         columns={props.columns || columns}
366 |         primaryKey={primaryKey}
367 |         loading={loading}
368 |         size={tableSize}
369 |         onRowClick={(record, index, e) => {
370 |           // fusion
371 |           onRowClick(record)
372 |           props.onRowClick?.(record, index, e)
373 |         }}
374 |       >
375 |         {''}
376 |       </Table>
377 |       {sources.map((column, key) => {
378 |         //专门用来承接对Column的状态管理
379 |         if (!isColumnComponent(column.schema)) return
380 |         return React.createElement(RecursionField, {
381 |           name: column.name,
382 |           schema: column.schema,
383 |           onlyRenderSelf: true,
384 |           key,
385 |         })
386 |       })}
387 |     </div>
388 |   )
389 | })
390 | 
391 | const TableColumn: React.FC<
392 |   React.PropsWithChildren<ISelectTableColumnProps>
393 | > = () => <></>
394 | 
395 | SelectTable.Column = TableColumn
396 | 
397 | export default SelectTable
398 | 
```

--------------------------------------------------------------------------------
/packages/element/src/array-base/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { ArrayField } from '@formily/core'
  2 | import { clone, isValid, uid } from '@formily/shared'
  3 | import {
  4 |   ExpressionScope,
  5 |   Fragment,
  6 |   h,
  7 |   useField,
  8 |   useFieldSchema,
  9 | } from '@formily/vue'
 10 | import {
 11 |   defineComponent,
 12 |   inject,
 13 |   InjectionKey,
 14 |   onBeforeUnmount,
 15 |   PropType,
 16 |   provide,
 17 |   Ref,
 18 |   ref,
 19 |   toRefs,
 20 | } from 'vue-demi'
 21 | import { stylePrefix } from '../__builtins__/configs'
 22 | 
 23 | import type { Schema } from '@formily/json-schema'
 24 | import type { Button as ButtonProps } from 'element-ui'
 25 | import { Button } from 'element-ui'
 26 | import { HandleDirective } from 'vue-slicksort'
 27 | import { composeExport } from '../__builtins__/shared'
 28 | 
 29 | export interface IArrayBaseAdditionProps extends ButtonProps {
 30 |   title?: string
 31 |   method?: 'push' | 'unshift'
 32 |   defaultValue?: any
 33 | }
 34 | 
 35 | export type ArrayBaseMixins = {
 36 |   Addition?: typeof ArrayBaseAddition
 37 |   Remove?: typeof ArrayBaseRemove
 38 |   MoveUp?: typeof ArrayBaseMoveUp
 39 |   MoveDown?: typeof ArrayBaseMoveDown
 40 |   SortHandle?: typeof ArrayBaseSortHandle
 41 |   Index?: typeof ArrayBaseIndex
 42 |   useArray?: typeof useArray
 43 |   useIndex?: typeof useIndex
 44 |   useRecord?: typeof useRecord
 45 | }
 46 | 
 47 | export interface IArrayBaseProps {
 48 |   disabled?: boolean
 49 |   keyMap?: WeakMap<Object, String> | String[] | null
 50 | }
 51 | 
 52 | export interface IArrayBaseItemProps {
 53 |   index: number
 54 |   record: any
 55 | }
 56 | 
 57 | export interface IArrayBaseContext {
 58 |   field: Ref<ArrayField>
 59 |   schema: Ref<Schema>
 60 |   props: IArrayBaseProps
 61 |   listeners: {
 62 |     [key in string]?: Function
 63 |   }
 64 |   keyMap?: WeakMap<Object, String> | String[] | null
 65 | }
 66 | 
 67 | const ArrayBaseSymbol: InjectionKey<IArrayBaseContext> =
 68 |   Symbol('ArrayBaseContext')
 69 | const ItemSymbol: InjectionKey<IArrayBaseItemProps> = Symbol('ItemContext')
 70 | 
 71 | const useArray = () => {
 72 |   return inject(ArrayBaseSymbol, null)
 73 | }
 74 | 
 75 | const useIndex = (index?: number) => {
 76 |   const { index: indexRef } = toRefs(inject(ItemSymbol))
 77 |   return indexRef ?? ref(index)
 78 | }
 79 | 
 80 | const useRecord = (record?: number) => {
 81 |   const { record: recordRef } = toRefs(inject(ItemSymbol))
 82 |   return recordRef ?? ref(record)
 83 | }
 84 | 
 85 | const isObjectValue = (schema: Schema) => {
 86 |   if (Array.isArray(schema?.items)) return isObjectValue(schema.items[0])
 87 | 
 88 |   if (schema?.items?.type === 'array' || schema?.items?.type === 'object') {
 89 |     return true
 90 |   }
 91 |   return false
 92 | }
 93 | 
 94 | const useKey = (schema: Schema) => {
 95 |   const isObject = isObjectValue(schema)
 96 |   let keyMap: WeakMap<Object, String> | String[] | null = null
 97 | 
 98 |   if (isObject) {
 99 |     keyMap = new WeakMap()
100 |   } else {
101 |     keyMap = []
102 |   }
103 | 
104 |   onBeforeUnmount(() => {
105 |     keyMap = null
106 |   })
107 | 
108 |   return {
109 |     keyMap,
110 |     getKey: (record: any, index?: number) => {
111 |       if (keyMap instanceof WeakMap) {
112 |         if (!keyMap.has(record)) {
113 |           keyMap.set(record, uid())
114 |         }
115 |         return `${keyMap.get(record)}-${index}`
116 |       }
117 | 
118 |       if (!keyMap[index]) {
119 |         keyMap[index] = uid()
120 |       }
121 | 
122 |       return `${keyMap[index]}-${index}`
123 |     },
124 |   }
125 | }
126 | 
127 | const getDefaultValue = (defaultValue: any, schema: Schema): any => {
128 |   if (isValid(defaultValue)) return clone(defaultValue)
129 |   if (Array.isArray(schema?.items))
130 |     return getDefaultValue(defaultValue, schema.items[0])
131 |   if (schema?.items?.type === 'array') return []
132 |   if (schema?.items?.type === 'boolean') return true
133 |   if (schema?.items?.type === 'date') return ''
134 |   if (schema?.items?.type === 'datetime') return ''
135 |   if (schema?.items?.type === 'number') return 0
136 |   if (schema?.items?.type === 'object') return {}
137 |   if (schema?.items?.type === 'string') return ''
138 |   return null
139 | }
140 | 
141 | const ArrayBaseInner = defineComponent<IArrayBaseProps>({
142 |   name: 'ArrayBase',
143 |   props: {
144 |     disabled: {
145 |       type: Boolean,
146 |       default: false,
147 |     },
148 |     keyMap: {
149 |       type: [WeakMap, Array] as PropType<WeakMap<Object, String> | String[]>,
150 |     },
151 |   },
152 |   setup(props, { slots, listeners }) {
153 |     const field = useField<ArrayField>()
154 |     const schema = useFieldSchema()
155 | 
156 |     provide(ArrayBaseSymbol, {
157 |       field,
158 |       schema,
159 |       props,
160 |       listeners,
161 |       keyMap: props.keyMap,
162 |     })
163 |     return () => {
164 |       return h(Fragment, {}, slots)
165 |     }
166 |   },
167 | })
168 | 
169 | const ArrayBaseItem = defineComponent({
170 |   name: 'ArrayBaseItem',
171 |   props: ['index', 'record'],
172 |   setup(props: IArrayBaseItemProps, { slots }) {
173 |     provide(ItemSymbol, props)
174 |     return () => {
175 |       return h(
176 |         ExpressionScope,
177 |         { props: { value: { $record: props.record, $index: props.index } } },
178 |         {
179 |           default: () => h(Fragment, {}, slots),
180 |         }
181 |       )
182 |     }
183 |   },
184 | })
185 | 
186 | const ArrayBaseSortHandle = defineComponent({
187 |   name: 'ArrayBaseSortHandle',
188 |   props: ['index'],
189 |   directives: {
190 |     handle: HandleDirective,
191 |   },
192 |   setup(props, { attrs }) {
193 |     const array = useArray()
194 |     const prefixCls = `${stylePrefix}-array-base`
195 | 
196 |     return () => {
197 |       if (!array) return null
198 |       if (array.field.value?.pattern !== 'editable') return null
199 | 
200 |       return h(
201 |         Button,
202 |         {
203 |           directives: [{ name: 'handle' }],
204 |           class: [`${prefixCls}-sort-handle`],
205 |           attrs: {
206 |             size: 'mini',
207 |             type: 'text',
208 |             icon: 'el-icon-rank',
209 |             ...attrs,
210 |           },
211 |         },
212 |         {}
213 |       )
214 |     }
215 |   },
216 | })
217 | 
218 | const ArrayBaseIndex = defineComponent({
219 |   name: 'ArrayBaseIndex',
220 |   setup(props, { attrs }) {
221 |     const index = useIndex()
222 |     const prefixCls = `${stylePrefix}-array-base`
223 |     return () => {
224 |       return h(
225 |         'span',
226 |         {
227 |           class: `${prefixCls}-index`,
228 |           attrs,
229 |         },
230 |         {
231 |           default: () => [`#${index.value + 1}.`],
232 |         }
233 |       )
234 |     }
235 |   },
236 | })
237 | 
238 | const ArrayBaseAddition = defineComponent({
239 |   name: 'ArrayBaseAddition',
240 |   props: ['title', 'method', 'defaultValue'],
241 |   setup(props: IArrayBaseAdditionProps, { listeners }) {
242 |     const self = useField()
243 |     const array = useArray()
244 |     const prefixCls = `${stylePrefix}-array-base`
245 |     return () => {
246 |       if (!array) return null
247 |       if (array?.field.value.pattern !== 'editable') return null
248 |       return h(
249 |         Button,
250 |         {
251 |           class: `${prefixCls}-addition`,
252 |           attrs: {
253 |             type: 'ghost',
254 |             icon: 'qax-icon-Alone-Plus',
255 |             ...props,
256 |           },
257 |           on: {
258 |             ...listeners,
259 |             click: (e) => {
260 |               if (array.props?.disabled) return
261 |               const defaultValue = getDefaultValue(
262 |                 props.defaultValue,
263 |                 array?.schema.value
264 |               )
265 |               if (props.method === 'unshift') {
266 |                 array?.field?.value.unshift(defaultValue)
267 |                 array.listeners?.add?.(0)
268 |               } else {
269 |                 array?.field?.value.push(defaultValue)
270 |                 array.listeners?.add?.(array?.field?.value?.value?.length - 1)
271 |               }
272 |               if (listeners.click) {
273 |                 listeners.click(e)
274 |               }
275 |             },
276 |           },
277 |         },
278 |         {
279 |           default: () => [self.value.title || props.title],
280 |         }
281 |       )
282 |     }
283 |   },
284 | })
285 | 
286 | const ArrayBaseRemove = defineComponent<
287 |   ButtonProps & { title?: string; index?: number }
288 | >({
289 |   name: 'ArrayBaseRemove',
290 |   props: ['title', 'index'],
291 |   setup(props, { attrs, listeners }) {
292 |     const indexRef = useIndex(props.index)
293 |     const base = useArray()
294 |     const prefixCls = `${stylePrefix}-array-base`
295 |     return () => {
296 |       if (base?.field.value.pattern !== 'editable') return null
297 |       return h(
298 |         Button,
299 |         {
300 |           class: `${prefixCls}-remove`,
301 |           attrs: {
302 |             type: 'text',
303 |             size: 'mini',
304 |             icon: 'el-icon-delete',
305 |             ...attrs,
306 |           },
307 |           on: {
308 |             ...listeners,
309 |             click: (e: MouseEvent) => {
310 |               e.stopPropagation()
311 |               if (Array.isArray(base?.keyMap)) {
312 |                 base?.keyMap?.splice(indexRef.value, 1)
313 |               }
314 | 
315 |               base?.field.value.remove(indexRef.value as number)
316 |               base?.listeners?.remove?.(indexRef.value as number)
317 | 
318 |               if (listeners.click) {
319 |                 listeners.click(e)
320 |               }
321 |             },
322 |           },
323 |         },
324 |         {
325 |           default: () => [props.title],
326 |         }
327 |       )
328 |     }
329 |   },
330 | })
331 | 
332 | const ArrayBaseMoveDown = defineComponent<
333 |   ButtonProps & { title?: string; index?: number }
334 | >({
335 |   name: 'ArrayBaseMoveDown',
336 |   props: ['title', 'index'],
337 |   setup(props, { attrs, listeners }) {
338 |     const indexRef = useIndex(props.index)
339 |     const base = useArray()
340 |     const prefixCls = `${stylePrefix}-array-base`
341 |     return () => {
342 |       if (base?.field.value.pattern !== 'editable') return null
343 |       return h(
344 |         Button,
345 |         {
346 |           class: `${prefixCls}-move-down`,
347 |           attrs: {
348 |             size: 'mini',
349 |             type: 'text',
350 |             icon: 'el-icon-arrow-down',
351 |             ...attrs,
352 |           },
353 |           on: {
354 |             ...listeners,
355 |             click: (e: MouseEvent) => {
356 |               e.stopPropagation()
357 |               if (Array.isArray(base?.keyMap)) {
358 |                 base.keyMap.splice(
359 |                   indexRef.value + 1,
360 |                   0,
361 |                   base.keyMap.splice(indexRef.value, 1)[0]
362 |                 )
363 |               }
364 | 
365 |               base?.field.value.moveDown(indexRef.value as number)
366 |               base?.listeners?.moveDown?.(indexRef.value as number)
367 | 
368 |               if (listeners.click) {
369 |                 listeners.click(e)
370 |               }
371 |             },
372 |           },
373 |         },
374 |         {
375 |           default: () => [props.title],
376 |         }
377 |       )
378 |     }
379 |   },
380 | })
381 | 
382 | const ArrayBaseMoveUp = defineComponent<
383 |   ButtonProps & { title?: string; index?: number }
384 | >({
385 |   name: 'ArrayBaseMoveUp',
386 |   props: ['title', 'index'],
387 |   setup(props, { attrs, listeners }) {
388 |     const indexRef = useIndex(props.index)
389 |     const base = useArray()
390 |     const prefixCls = `${stylePrefix}-array-base`
391 |     return () => {
392 |       if (base?.field.value.pattern !== 'editable') return null
393 |       return h(
394 |         Button,
395 |         {
396 |           class: `${prefixCls}-move-up`,
397 |           attrs: {
398 |             size: 'mini',
399 |             type: 'text',
400 |             icon: 'el-icon-arrow-up',
401 |             ...attrs,
402 |           },
403 |           on: {
404 |             ...listeners,
405 |             click: (e: MouseEvent) => {
406 |               e.stopPropagation()
407 |               if (Array.isArray(base?.keyMap)) {
408 |                 base.keyMap.splice(
409 |                   indexRef.value - 1,
410 |                   0,
411 |                   base.keyMap.splice(indexRef.value, 1)[0]
412 |                 )
413 |               }
414 | 
415 |               base?.field.value.moveUp(indexRef.value as number)
416 |               base?.listeners?.moveUp?.(indexRef.value as number)
417 | 
418 |               if (listeners.click) {
419 |                 listeners.click(e)
420 |               }
421 |             },
422 |           },
423 |         },
424 |         {
425 |           default: () => [props.title],
426 |         }
427 |       )
428 |     }
429 |   },
430 | })
431 | 
432 | export const ArrayBase = composeExport(ArrayBaseInner, {
433 |   Index: ArrayBaseIndex,
434 |   Item: ArrayBaseItem,
435 |   SortHandle: ArrayBaseSortHandle,
436 |   Addition: ArrayBaseAddition,
437 |   Remove: ArrayBaseRemove,
438 |   MoveDown: ArrayBaseMoveDown,
439 |   MoveUp: ArrayBaseMoveUp,
440 |   useArray: useArray,
441 |   useIndex: useIndex,
442 |   useKey: useKey,
443 |   useRecord: useRecord,
444 | })
445 | 
446 | export default ArrayBase
447 | 
```

--------------------------------------------------------------------------------
/packages/path/src/parser.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Tokenizer } from './tokenizer'
  2 | import {
  3 |   Token,
  4 |   nameTok,
  5 |   colonTok,
  6 |   dotTok,
  7 |   starTok,
  8 |   bangTok,
  9 |   bracketLTok,
 10 |   bracketRTok,
 11 |   braceLTok,
 12 |   braceRTok,
 13 |   bracketDLTok,
 14 |   parenLTok,
 15 |   parenRTok,
 16 |   commaTok,
 17 |   expandTok,
 18 |   eofTok,
 19 |   dbStarTok,
 20 | } from './tokens'
 21 | import { bracketArrayContext, destructorContext } from './contexts'
 22 | import {
 23 |   IdentifierNode,
 24 |   ExpandOperatorNode,
 25 |   WildcardOperatorNode,
 26 |   RangeExpressionNode,
 27 |   GroupExpressionNode,
 28 |   DotOperatorNode,
 29 |   IgnoreExpressionNode,
 30 |   DestructorExpressionNode,
 31 |   ObjectPatternNode,
 32 |   ObjectPatternPropertyNode,
 33 |   ArrayPatternNode,
 34 |   Node,
 35 |   Segments,
 36 | } from './types'
 37 | import { parseDestructorRules, setDestructor } from './destructor'
 38 | import { isNumberLike } from './shared'
 39 | import { Path } from './index'
 40 | 
 41 | const createTreeBySegments = (segments: Segments = [], afterNode?: Node) => {
 42 |   const segLen = segments.length
 43 |   const build = (start = 0) => {
 44 |     const after = start < segLen - 1 ? build(start + 1) : afterNode
 45 |     const dot = after && {
 46 |       type: 'DotOperator',
 47 |       after,
 48 |     }
 49 |     return {
 50 |       type: 'Identifier',
 51 |       value: segments[start],
 52 |       after: dot,
 53 |     }
 54 |   }
 55 |   return build()
 56 | }
 57 | 
 58 | const calculate = (
 59 |   a: string | number,
 60 |   b: string | number,
 61 |   operator: string
 62 | ) => {
 63 |   if (isNumberLike(a) && isNumberLike(b)) {
 64 |     if (operator === '+') return String(Number(a) + Number(b))
 65 |     if (operator === '-') return String(Number(a) - Number(b))
 66 |     if (operator === '*') return String(Number(a) * Number(b))
 67 |     if (operator === '/') return String(Number(a) / Number(b))
 68 |   } else {
 69 |     if (operator === '+') return String(a) + String(b)
 70 |     if (operator === '-') return 'NaN'
 71 |     if (operator === '*') return 'NaN'
 72 |     if (operator === '/') return 'NaN'
 73 |   }
 74 |   return String(Number(b))
 75 | }
 76 | 
 77 | export class Parser extends Tokenizer {
 78 |   public isMatchPattern = false
 79 | 
 80 |   public isWildMatchPattern = false
 81 | 
 82 |   public haveExcludePattern = false
 83 | 
 84 |   public haveRelativePattern = false
 85 | 
 86 |   public base: Path
 87 | 
 88 |   public relative: string | number
 89 | 
 90 |   public data: {
 91 |     segments: Segments
 92 |     tree?: Node
 93 |   }
 94 | 
 95 |   constructor(input: string, base?: Path) {
 96 |     super(input)
 97 |     this.base = base
 98 |   }
 99 | 
100 |   parse() {
101 |     let node: Node
102 |     this.data = {
103 |       segments: [],
104 |     }
105 |     if (!this.eat(eofTok)) {
106 |       this.next()
107 |       node = this.parseAtom(this.state.type)
108 |     }
109 |     this.data.tree = node
110 | 
111 |     return node
112 |   }
113 | 
114 |   append(parent: Node, node: Node) {
115 |     if (parent && node) {
116 |       parent.after = node
117 |     }
118 |   }
119 | 
120 |   parseAtom(type: Token): Node {
121 |     switch (type) {
122 |       case braceLTok:
123 |       case bracketLTok:
124 |         if (this.includesContext(destructorContext)) {
125 |           if (type === braceLTok) {
126 |             return this.parseObjectPattern()
127 |           } else {
128 |             return this.parseArrayPattern()
129 |           }
130 |         }
131 |         return this.parseDestructorExpression()
132 |       case nameTok:
133 |         return this.parseIdentifier()
134 |       case expandTok:
135 |         return this.parseExpandOperator()
136 |       case dbStarTok:
137 |       case starTok:
138 |         return this.parseWildcardOperator()
139 |       case bracketDLTok:
140 |         return this.parseIgnoreExpression()
141 |       case dotTok:
142 |         return this.parseDotOperator()
143 |     }
144 |   }
145 | 
146 |   pushSegments(key: string | number) {
147 |     this.data.segments.push(key)
148 |   }
149 | 
150 |   parseIdentifier() {
151 |     const node: IdentifierNode = {
152 |       type: 'Identifier',
153 |       value: this.state.value,
154 |     }
155 |     const hasNotInDestructor =
156 |       !this.includesContext(destructorContext) &&
157 |       !this.isMatchPattern &&
158 |       !this.isWildMatchPattern
159 | 
160 |     this.next()
161 |     if (this.includesContext(bracketArrayContext)) {
162 |       if (this.state.type !== bracketRTok) {
163 |         throw this.unexpect()
164 |       } else {
165 |         this.state.context.pop()
166 |         this.next()
167 |       }
168 |     } else if (hasNotInDestructor) {
169 |       this.pushSegments(node.value)
170 |     }
171 |     if (this.state.type === bracketLTok) {
172 |       this.next()
173 |       if (this.state.type !== nameTok) {
174 |         throw this.unexpect()
175 |       }
176 |       this.state.context.push(bracketArrayContext)
177 |       let isNumberKey = false
178 |       if (/^\d+$/.test(this.state.value)) {
179 |         isNumberKey = true
180 |       }
181 |       const value = this.state.value
182 |       this.pushSegments(isNumberKey ? Number(value) : value)
183 |       const after = this.parseAtom(this.state.type) as IdentifierNode
184 |       if (isNumberKey) {
185 |         after.arrayIndex = true
186 |       }
187 |       this.append(node, after)
188 |     } else {
189 |       this.append(node, this.parseAtom(this.state.type))
190 |     }
191 | 
192 |     return node
193 |   }
194 | 
195 |   parseExpandOperator() {
196 |     const node: ExpandOperatorNode = {
197 |       type: 'ExpandOperator',
198 |     }
199 | 
200 |     this.isMatchPattern = true
201 |     this.isWildMatchPattern = true
202 |     this.data.segments = []
203 | 
204 |     this.next()
205 | 
206 |     this.append(node, this.parseAtom(this.state.type))
207 | 
208 |     return node
209 |   }
210 | 
211 |   parseWildcardOperator(): WildcardOperatorNode {
212 |     const node: WildcardOperatorNode = {
213 |       type: 'WildcardOperator',
214 |     }
215 | 
216 |     if (this.state.type === dbStarTok) {
217 |       node.optional = true
218 |     }
219 | 
220 |     this.isMatchPattern = true
221 |     this.isWildMatchPattern = true
222 |     this.data.segments = []
223 | 
224 |     this.next()
225 | 
226 |     if (this.state.type === parenLTok) {
227 |       node.filter = this.parseGroupExpression(node)
228 |     } else if (this.state.type === bracketLTok) {
229 |       node.filter = this.parseRangeExpression(node)
230 |     }
231 | 
232 |     this.append(node, this.parseAtom(this.state.type))
233 | 
234 |     return node
235 |   }
236 | 
237 |   parseDestructorExpression(): DestructorExpressionNode {
238 |     const node: DestructorExpressionNode = {
239 |       type: 'DestructorExpression',
240 |     }
241 |     this.state.context.push(destructorContext)
242 |     const startPos = this.state.pos - 1
243 |     node.value =
244 |       this.state.type === braceLTok
245 |         ? this.parseObjectPattern()
246 |         : this.parseArrayPattern()
247 |     const endPos = this.state.pos
248 |     this.state.context.pop()
249 |     node.source = this.input
250 |       .substring(startPos, endPos)
251 |       .replace(
252 |         /\[\s*([\+\-\*\/])?\s*([^,\]\s]*)\s*\]/,
253 |         (match, operator, target) => {
254 |           if (this.relative !== undefined) {
255 |             if (operator) {
256 |               if (target) {
257 |                 return calculate(this.relative, target, operator)
258 |               } else {
259 |                 return calculate(this.relative, 1, operator)
260 |               }
261 |             } else {
262 |               if (target) {
263 |                 return calculate(this.relative, target, '+')
264 |               } else {
265 |                 return String(this.relative)
266 |               }
267 |             }
268 |           }
269 |           return match
270 |         }
271 |       )
272 |       .replace(/\s*\.\s*/g, '')
273 |       .replace(/\s*/g, '')
274 |     if (this.relative === undefined) {
275 |       setDestructor(node.source, parseDestructorRules(node))
276 |     }
277 |     this.relative = undefined
278 |     this.pushSegments(node.source)
279 |     this.next()
280 |     this.append(node, this.parseAtom(this.state.type))
281 |     return node
282 |   }
283 | 
284 |   parseArrayPattern(): ArrayPatternNode {
285 |     const node: ArrayPatternNode = {
286 |       type: 'ArrayPattern',
287 |       elements: [],
288 |     }
289 |     this.next()
290 |     node.elements = this.parseArrayPatternElements()
291 |     return node
292 |   }
293 | 
294 |   parseArrayPatternElements() {
295 |     const nodes = []
296 |     while (this.state.type !== bracketRTok && this.state.type !== eofTok) {
297 |       nodes.push(this.parseAtom(this.state.type))
298 |       if (this.state.type === bracketRTok) {
299 |         if (this.includesContext(destructorContext)) {
300 |           this.next()
301 |         }
302 |         return nodes
303 |       }
304 |       this.next()
305 |     }
306 |     return nodes
307 |   }
308 | 
309 |   parseObjectPattern(): ObjectPatternNode {
310 |     const node: ObjectPatternNode = {
311 |       type: 'ObjectPattern',
312 |       properties: [],
313 |     }
314 |     this.next()
315 |     node.properties = this.parseObjectProperties()
316 |     return node
317 |   }
318 | 
319 |   parseObjectProperties(): ObjectPatternPropertyNode[] {
320 |     const nodes = []
321 |     while (this.state.type !== braceRTok && this.state.type !== eofTok) {
322 |       const node: ObjectPatternPropertyNode = {
323 |         type: 'ObjectPatternProperty',
324 |         key: this.parseAtom(this.state.type) as IdentifierNode,
325 |       }
326 |       nodes.push(node)
327 |       if (this.state.type === colonTok) {
328 |         this.next()
329 |         node.value = this.parseAtom(this.state.type) as
330 |           | IdentifierNode
331 |           | ObjectPatternNode[]
332 |           | ArrayPatternNode[]
333 |       }
334 |       if (this.state.type === braceRTok) {
335 |         if (this.includesContext(destructorContext)) {
336 |           this.next()
337 |         }
338 |         return nodes
339 |       }
340 |       this.next()
341 |     }
342 |     return nodes
343 |   }
344 | 
345 |   parseDotOperator(): Node {
346 |     const node: DotOperatorNode = {
347 |       type: 'DotOperator',
348 |     }
349 | 
350 |     const prevToken = this.type_
351 |     if (!prevToken && this.base) {
352 |       if (this.base.isMatchPattern) {
353 |         throw new Error('Base path must be an absolute path.')
354 |       }
355 |       this.data.segments = this.base.toArr()
356 |       while (this.state.type === dotTok) {
357 |         this.relative = this.data.segments.pop()
358 |         this.haveRelativePattern = true
359 |         this.next()
360 |       }
361 |       return createTreeBySegments(
362 |         this.data.segments.slice(),
363 |         this.parseAtom(this.state.type)
364 |       )
365 |     } else {
366 |       this.next()
367 |     }
368 | 
369 |     this.append(node, this.parseAtom(this.state.type))
370 | 
371 |     return node
372 |   }
373 | 
374 |   parseIgnoreExpression() {
375 |     this.next()
376 | 
377 |     const value = String(this.state.value).replace(/\s*/g, '')
378 | 
379 |     const node: IgnoreExpressionNode = {
380 |       type: 'IgnoreExpression',
381 |       value: value,
382 |     }
383 | 
384 |     this.pushSegments(value)
385 | 
386 |     this.next()
387 | 
388 |     this.append(node, this.parseAtom(this.state.type))
389 | 
390 |     this.next()
391 | 
392 |     return node
393 |   }
394 | 
395 |   parseGroupExpression(parent: Node) {
396 |     const node: GroupExpressionNode = {
397 |       type: 'GroupExpression',
398 |       value: [],
399 |     }
400 | 
401 |     this.isMatchPattern = true
402 |     this.data.segments = []
403 | 
404 |     this.next()
405 | 
406 |     loop: while (true) {
407 |       switch (this.state.type) {
408 |         case commaTok:
409 |           this.next()
410 |           break
411 |         case bangTok:
412 |           node.isExclude = true
413 |           this.haveExcludePattern = true
414 |           this.next()
415 |           break
416 |         case eofTok:
417 |           break loop
418 |         case parenRTok:
419 |           break loop
420 |         default:
421 |           node.value.push(this.parseAtom(this.state.type))
422 |       }
423 |     }
424 | 
425 |     this.next()
426 | 
427 |     this.append(parent, this.parseAtom(this.state.type))
428 | 
429 |     return node
430 |   }
431 | 
432 |   parseRangeExpression(parent: Node) {
433 |     const node: RangeExpressionNode = {
434 |       type: 'RangeExpression',
435 |     }
436 | 
437 |     this.next()
438 | 
439 |     this.isMatchPattern = true
440 |     this.data.segments = []
441 | 
442 |     let start = false,
443 |       hasColon = false
444 | 
445 |     loop: while (true) {
446 |       switch (this.state.type) {
447 |         case colonTok:
448 |           hasColon = true
449 |           start = true
450 |           this.next()
451 |           break
452 |         case bracketRTok:
453 |           if (!hasColon && !node.end) {
454 |             node.end = node.start
455 |           }
456 |           break loop
457 |         case commaTok:
458 |           // never reach
459 |           throw this.unexpect()
460 |         case eofTok:
461 |           // never reach
462 |           break loop
463 |         default:
464 |           if (!start) {
465 |             node.start = this.parseAtom(this.state.type) as IdentifierNode
466 |           } else {
467 |             node.end = this.parseAtom(this.state.type) as IdentifierNode
468 |           }
469 |       }
470 |     }
471 | 
472 |     this.next()
473 | 
474 |     this.append(parent, this.parseAtom(this.state.type))
475 | 
476 |     return node
477 |   }
478 | }
479 | 
```

--------------------------------------------------------------------------------
/packages/element/src/form-item/style.scss:
--------------------------------------------------------------------------------

```scss
  1 | @use 'sass:math';
  2 | @import '../__builtins__/styles/common.scss';
  3 | @import './var.scss';
  4 | @import './grid.scss';
  5 | @import './animation.scss';
  6 | 
  7 | .#{$form-item-prefix} {
  8 |   display: flex;
  9 |   margin-bottom: $--form-item-margin-bottom;
 10 |   position: relative;
 11 |   line-height: $--form-item-medium-line-height;
 12 |   font-size: $--form-font-size;
 13 | 
 14 |   &-label * {
 15 |     line-height: $--form-item-medium-line-height;
 16 |   }
 17 | 
 18 |   &-label-content {
 19 |     min-height: $--form-item-medium-line-height;
 20 |   }
 21 | 
 22 |   &-content-component {
 23 |     line-height: $--form-item-medium-line-height;
 24 |   }
 25 | 
 26 |   .#{$namespace}-input,
 27 |   .#{$namespace}-input-number,
 28 |   .#{$namespace}-input-number.is-controls-right,
 29 |   .#{$namespace}-select,
 30 |   .#{$namespace}-cascader,
 31 |   .#{$namespace}-date-editor--daterange,
 32 |   .#{$namespace}-date-editor--timerange,
 33 |   .#{$namespace}-date-editor--datetimerange,
 34 |   .#{$namespace}-date-editor.#{$namespace}-input,
 35 |   .#{$namespace}-date-editor.#{$namespace}-input__inner,
 36 |   .#{$namespace}-tree-select {
 37 |     width: 100%;
 38 |   }
 39 | 
 40 |   .#{$namespace}-input-group {
 41 |     vertical-align: top;
 42 |   }
 43 | }
 44 | 
 45 | .#{$form-item-prefix}-label {
 46 |   position: relative;
 47 |   display: flex;
 48 | 
 49 |   &-content {
 50 |     overflow: hidden;
 51 |     text-overflow: ellipsis;
 52 |     white-space: nowrap;
 53 |   }
 54 | 
 55 |   &-tooltip {
 56 |     cursor: help;
 57 | 
 58 |     * {
 59 |       cursor: help;
 60 |     }
 61 | 
 62 |     label {
 63 |       border-bottom: 1px dashed currentColor;
 64 |     }
 65 |   }
 66 | }
 67 | 
 68 | .#{$form-item-prefix}-label label {
 69 |   color: $--color-text-regular;
 70 | }
 71 | 
 72 | .#{$form-item-prefix}-label-align-left {
 73 |   > .#{$form-item-prefix}-label {
 74 |     justify-content: flex-start;
 75 |   }
 76 | }
 77 | 
 78 | .#{$form-item-prefix}-label-align-right {
 79 |   > .#{$form-item-prefix}-label {
 80 |     justify-content: flex-end;
 81 |   }
 82 | }
 83 | 
 84 | .#{$form-item-prefix}-label-wrap {
 85 |   .#{$form-item-prefix}-label {
 86 |     label {
 87 |       white-space: pre-line;
 88 |     }
 89 |   }
 90 | }
 91 | 
 92 | .#{$form-item-prefix}-feedback-layout-terse {
 93 |   margin-bottom: 8px;
 94 | 
 95 |   &.#{$form-item-prefix}-feedback-has-text:not(.#{$form-item-prefix}-inset) {
 96 |     margin-bottom: 0;
 97 |   }
 98 | }
 99 | 
100 | .#{$form-item-prefix}-feedback-layout-loose {
101 |   margin-bottom: $--form-error-line-height;
102 | 
103 |   &.#{$form-item-prefix}-feedback-has-text:not(.#{$form-item-prefix}-inset) {
104 |     margin-bottom: 0;
105 |   }
106 | }
107 | 
108 | .#{$form-item-prefix}-feedback-layout-none {
109 |   margin-bottom: 0;
110 | 
111 |   &.#{$form-item-prefix}-feedback-has-text:not(.#{$form-item-prefix}-inset) {
112 |     margin-bottom: 0;
113 |   }
114 | }
115 | 
116 | .#{$form-item-prefix}-control {
117 |   width: 100%;
118 |   flex: 1;
119 | 
120 |   .#{$form-item-prefix}-control-content {
121 |     display: flex;
122 | 
123 |     .#{$form-item-prefix}-control-content-component {
124 |       width: 100%;
125 |       min-height: $--form-item-medium-line-height;
126 |       line-height: $--form-item-medium-line-height;
127 | 
128 |       &-has-feedback-icon {
129 |         flex: 1;
130 |         position: relative;
131 |         display: flex;
132 |         align-items: center;
133 |       }
134 |     }
135 | 
136 |     .#{$form-item-prefix}-addon-before {
137 |       margin-right: 8px;
138 |       display: inline-flex;
139 |       align-items: center;
140 |       min-height: $--form-item-medium-line-height;
141 |       flex-shrink: 0;
142 |     }
143 | 
144 |     .#{$form-item-prefix}-addon-after {
145 |       margin-left: 8px;
146 |       display: inline-flex;
147 |       align-items: center;
148 |       min-height: $--form-item-medium-line-height;
149 |       flex-shrink: 0;
150 |     }
151 |   }
152 | }
153 | 
154 | .#{$form-item-prefix}-size-small {
155 |   font-size: $--font-size-extra-small;
156 | 
157 |   .#{$form-item-prefix}-label * {
158 |     line-height: $--form-item-small-line-height;
159 |   }
160 | 
161 |   .#{$form-item-prefix}-label-content {
162 |     min-height: $--form-item-small-line-height;
163 |   }
164 | 
165 |   .#{$form-item-prefix}-control-content {
166 |     .#{$form-item-prefix}-control-content-component {
167 |       line-height: $--form-item-small-line-height;
168 |       min-height: $--form-item-small-line-height;
169 |     }
170 |   }
171 | 
172 |   .#{$form-item-prefix}-help,
173 |   .#{$form-item-prefix}-extra {
174 |     min-height: $--form-error-line-height;
175 |   }
176 | 
177 |   .#{$form-item-prefix}-control-content {
178 |     min-height: $--form-item-small-line-height;
179 |   }
180 | 
181 |   .#{$form-item-prefix}-label > label {
182 |     height: $--form-item-small-line-height;
183 |   }
184 | 
185 |   .#{$namespace}-input {
186 |     input {
187 |       height: $--form-item-small-line-height;
188 |       line-height: $--form-item-small-line-height;
189 |     }
190 |   }
191 | 
192 |   .#{$namespace}-input-number {
193 |     line-height: $--form-item-small-line-height;
194 |     &.is-controls-right {
195 |       .#{$namespace}-input-number__increase,
196 |       .#{$namespace}-input-number__decrease {
197 |         line-height: math.div($--form-item-small-line-height, 2);
198 |         height: math.div($--form-item-small-line-height, 2);
199 |         font-size: $--font-size-extra-small;
200 |         box-sizing: border-box;
201 |       }
202 |     }
203 |   }
204 | }
205 | 
206 | .#{$form-item-prefix}-size-large {
207 |   font-size: $--font-size-medium;
208 | 
209 |   .#{$form-item-prefix}-label * {
210 |     line-height: $--form-item-large-line-height;
211 |   }
212 | 
213 |   .#{$form-item-prefix}-label-content {
214 |     min-height: $--form-item-large-line-height;
215 |   }
216 | 
217 |   .#{$form-item-prefix}-control-content {
218 |     .#{$form-item-prefix}-control-content-component {
219 |       line-height: $--form-item-large-line-height;
220 |       min-height: $--form-item-large-line-height;
221 |     }
222 |   }
223 | 
224 |   .#{$form-item-prefix}-help,
225 |   .#{$form-item-prefix}-extra {
226 |     min-height: $--form-error-line-height;
227 |   }
228 | 
229 |   .#{$form-item-prefix}-control-content {
230 |     min-height: $--form-item-large-line-height;
231 |   }
232 | 
233 |   .#{$namespace}-input {
234 |     input {
235 |       height: $--form-item-large-line-height;
236 |       line-height: $--form-item-large-line-height;
237 |     }
238 |   }
239 | 
240 |   .#{$namespace}-select {
241 |     input {
242 |       height: $--form-item-large-line-height !important;
243 |       line-height: $--form-item-large-line-height;
244 |     }
245 |   }
246 | 
247 |   .#{$namespace}-select__tags .el-tag {
248 |     height: $--form-item-large-line-height - 12px;
249 |     line-height: $--form-item-large-line-height - 12px;
250 |   }
251 | 
252 |   .#{$namespace}-input-number {
253 |     line-height: $--form-item-large-line-height;
254 |     &.is-controls-right {
255 |       .#{$namespace}-input-number__increase,
256 |       .#{$namespace}-input-number__decrease {
257 |         line-height: math.div($--form-item-large-line-height, 2) - 1;
258 |         font-size: $--font-size-medium;
259 |       }
260 |     }
261 |   }
262 | }
263 | 
264 | .#{$form-item-prefix} {
265 |   &-layout-vertical {
266 |     display: block;
267 | 
268 |     .#{$form-item-prefix}-label * {
269 |       line-height: $--form-item-label-top-line-height;
270 |     }
271 | 
272 |     .#{$form-item-prefix}-label-content {
273 |       min-height: $--form-item-label-top-line-height;
274 |     }
275 |   }
276 | }
277 | 
278 | .#{$form-item-prefix}-feedback-layout-popover {
279 |   margin-bottom: 8px;
280 | }
281 | 
282 | .#{$form-item-prefix}-label-tooltip {
283 |   margin-left: 4px;
284 |   color: $--color-text-secondary;
285 |   display: flex;
286 |   align-items: center;
287 |   height: $--form-item-medium-line-height;
288 |   cursor: pointer;
289 |   i {
290 |     line-height: 1;
291 |   }
292 | }
293 | 
294 | .#{$form-item-prefix}-control-align-left {
295 |   .#{$form-item-prefix}-control-content {
296 |     justify-content: flex-start;
297 |   }
298 | }
299 | 
300 | .#{$form-item-prefix}-control-align-right {
301 |   .#{$form-item-prefix}-control-content {
302 |     justify-content: flex-end;
303 |   }
304 | }
305 | 
306 | .#{$form-item-prefix}-control-wrap {
307 |   .#{$form-item-prefix}-control {
308 |     white-space: pre-line;
309 |   }
310 | }
311 | 
312 | .#{$form-item-prefix}-asterisk {
313 |   color: $--color-danger;
314 |   margin-right: 4px;
315 |   display: inline-block;
316 |   font-family: SimSun, sans-serif;
317 | }
318 | 
319 | .#{$form-item-prefix}-colon {
320 |   margin-left: 2px;
321 |   margin-right: 8px;
322 | }
323 | 
324 | .#{$form-item-prefix}-help,
325 | .#{$form-item-prefix}-extra {
326 |   clear: both;
327 |   min-height: $--form-error-line-height;
328 |   line-height: $--form-error-line-height;
329 |   color: $--color-text-secondary;
330 |   transition: $--color-transition-base;
331 |   padding-top: 0;
332 | }
333 | 
334 | .#{$form-item-prefix}-fullness {
335 |   > .#{$form-item-prefix}-control {
336 |     > .#{$form-item-prefix}-control-content {
337 |       > .#{$form-item-prefix}-control-content-component {
338 |         > *:first-child {
339 |           width: 100%;
340 |         }
341 |       }
342 |     }
343 |   }
344 | }
345 | 
346 | .#{$form-item-prefix}-control-content-component-has-feedback-icon {
347 |   border-radius: $--border-radius-base;
348 |   border: $--border-base;
349 |   padding-right: 8px;
350 |   transition: $--all-transition;
351 |   touch-action: manipulation;
352 |   outline: none;
353 | 
354 |   .#{$namespace}-input-number,
355 |   .#{$namespace}-date-editor .#{$namespace}-input__inner,
356 |   .#{$namespace}-select .#{$namespace}-input__inner,
357 |   .#{$namespace}-input .#{$namespace}-input__inner {
358 |     border: none !important;
359 |     box-shadow: none !important;
360 |   }
361 |   .#{$namespace}-input-number.is-controls-right .#{$namespace}-input__inner {
362 |     padding-right: 40px;
363 |   }
364 |   .#{$namespace}-input-number.is-controls-right
365 |     .#{$namespace}-input-number__increase {
366 |     top: 0;
367 |     right: 8px;
368 |     border-right: $--border-base;
369 |   }
370 |   .#{$namespace}-input-number.is-controls-right
371 |     .#{$namespace}-input-number__decrease {
372 |     bottom: 0;
373 |     right: 8px;
374 |     border-right: $--border-base;
375 |   }
376 | }
377 | 
378 | .#{$form-item-prefix} {
379 |   &:hover {
380 |     .#{$form-item-prefix}-control-content-component-has-feedback-icon {
381 |       @include hover;
382 |     }
383 |   }
384 | }
385 | 
386 | .#{$form-item-prefix}-active {
387 |   .#{$form-item-prefix}-control-content-component-has-feedback-icon {
388 |     @include active;
389 |   }
390 | }
391 | 
392 | .#{$form-item-prefix}-error {
393 |   & .#{$namespace}-input__inner,
394 |   & .#{$namespace}-textarea__inner {
395 |     &,
396 |     &.hover {
397 |       border-color: $--color-danger;
398 |     }
399 |   }
400 | 
401 |   & .#{$namespace}-input__inner,
402 |   & .#{$namespace}-textarea__inner {
403 |     &:focus {
404 |       border-color: $--color-danger;
405 |     }
406 |   }
407 | 
408 |   & .#{$namespace}-input-group__append,
409 |   & .#{$namespace}-input-group__prepend {
410 |     & .#{$namespace}-input__inner {
411 |       border-color: transparent;
412 |     }
413 |   }
414 |   .#{$namespace}-input__validateIcon {
415 |     color: $--color-danger !important;
416 |   }
417 | }
418 | 
419 | .#{$form-item-prefix}-error-help,
420 | .#{$form-item-prefix}-warning-help,
421 | .#{$form-item-prefix}-success-help {
422 |   i {
423 |     margin-right: 8px;
424 |   }
425 | }
426 | 
427 | .#{$form-item-prefix}-error-help {
428 |   color: $--color-danger;
429 | }
430 | 
431 | .#{$form-item-prefix}-warning-help {
432 |   color: $--color-warning;
433 | }
434 | 
435 | .#{$form-item-prefix}-success-help {
436 |   color: $--color-success;
437 | }
438 | 
439 | .#{$form-item-prefix}-warning {
440 |   & .#{$namespace}-input__inner,
441 |   & .#{$namespace}-textarea__inner {
442 |     &,
443 |     &.hover {
444 |       border-color: $--color-warning;
445 |     }
446 |   }
447 | 
448 |   & .#{$namespace}-input__inner,
449 |   & .#{$namespace}-textarea__inner {
450 |     &:focus {
451 |       border-color: $--color-warning;
452 |     }
453 |   }
454 | 
455 |   & .#{$namespace}-input-group__append,
456 |   & .#{$namespace}-input-group__prepend {
457 |     & .#{$namespace}-input__inner {
458 |       border-color: transparent;
459 |     }
460 |   }
461 |   .#{$namespace}-input__validateIcon {
462 |     color: $--color-warning !important;
463 |   }
464 | }
465 | 
466 | .#{$form-item-prefix}-success {
467 |   & .#{$namespace}-input__inner,
468 |   & .#{$namespace}-textarea__inner {
469 |     &,
470 |     &.hover {
471 |       border-color: $--color-success;
472 |     }
473 |   }
474 | 
475 |   & .#{$namespace}-input__inner,
476 |   & .#{$namespace}-textarea__inner {
477 |     &:focus {
478 |       border-color: $--color-success;
479 |     }
480 |   }
481 | 
482 |   & .#{$namespace}-input-group__append,
483 |   & .#{$namespace}-input-group__prepend {
484 |     & .#{$namespace}-input__inner {
485 |       border-color: transparent;
486 |     }
487 |   }
488 |   .#{$namespace}-input__validateIcon {
489 |     color: $--color-success !important;
490 |   }
491 | }
492 | 
493 | .#{$form-item-prefix}-bordered-none {
494 |   .#{$namespace}-input__inner {
495 |     border: none !important;
496 |   }
497 | 
498 |   .#{$namespace}-input-number__decrease,
499 |   .#{$namespace}-input-number__increase {
500 |     border: none !important;
501 |     background: transparent !important;
502 |   }
503 | }
504 | 
505 | .#{$form-item-prefix}-inset {
506 |   border-radius: $--border-radius-base;
507 |   border: $--border-base;
508 |   padding-left: 12px;
509 |   transition: 0.3s all;
510 | 
511 |   &:hover {
512 |     @include hover;
513 |   }
514 | }
515 | 
516 | .#{$form-item-prefix}-inset-active {
517 |   @include active;
518 | }
519 | 
```

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

```typescript
  1 | import React, { useState, useMemo } from 'react'
  2 | import {
  3 |   observer,
  4 |   useFieldSchema,
  5 |   useField,
  6 |   Schema,
  7 |   RecursionField,
  8 | } from '@formily/react'
  9 | import cls from 'classnames'
 10 | import { GeneralField, FieldDisplayTypes } from '@formily/core'
 11 | import { isArr, isBool, isFn } from '@formily/shared'
 12 | import { Input, Table } from 'antd'
 13 | import { TableProps, ColumnProps } from 'antd/lib/table'
 14 | import { SearchProps } from 'antd/lib/input'
 15 | import { useFilterOptions } from './useFilterOptions'
 16 | import { useFlatOptions } from './useFlatOptions'
 17 | import { useSize } from './useSize'
 18 | import { useTitleAddon } from './useTitleAddon'
 19 | import { useCheckSlackly, getIndeterminate } from './useCheckSlackly'
 20 | import { getUISelected, getOutputData } from './utils'
 21 | import { usePrefixCls } from '../__builtins__'
 22 | 
 23 | const { Search } = Input
 24 | 
 25 | interface ObservableColumnSource {
 26 |   field: GeneralField
 27 |   columnProps: ColumnProps<any>
 28 |   schema: Schema
 29 |   display: FieldDisplayTypes
 30 |   name: string
 31 | }
 32 | 
 33 | type IFilterOption = boolean | ((option: any, keyword: string) => boolean)
 34 | 
 35 | type IFilterSort = (optionA: any, optionB: any) => number
 36 | 
 37 | export interface ISelectTableColumnProps extends ColumnProps<any> {
 38 |   key: React.ReactText
 39 | }
 40 | 
 41 | export interface ISelectTableProps extends TableProps<any> {
 42 |   mode?: 'multiple' | 'single'
 43 |   dataSource?: any[]
 44 |   optionAsValue?: boolean
 45 |   valueType?: 'all' | 'parent' | 'child' | 'path'
 46 |   showSearch?: boolean
 47 |   searchProps?: SearchProps
 48 |   primaryKey?: string | ((record: any) => string)
 49 |   filterOption?: IFilterOption
 50 |   filterSort?: IFilterSort
 51 |   onSearch?: (keyword: string) => void
 52 |   onChange?: (value: any, options: any) => void
 53 |   value?: any
 54 | }
 55 | 
 56 | type ComposedSelectTable = React.FC<
 57 |   React.PropsWithChildren<ISelectTableProps>
 58 | > & {
 59 |   Column?: React.FC<React.PropsWithChildren<ISelectTableColumnProps>>
 60 | }
 61 | 
 62 | const isColumnComponent = (schema: Schema) => {
 63 |   return schema['x-component']?.indexOf('Column') > -1
 64 | }
 65 | 
 66 | const useSources = () => {
 67 |   const arrayField = useField()
 68 |   const schema = useFieldSchema()
 69 |   const parseSources = (schema: Schema): ObservableColumnSource[] => {
 70 |     if (isColumnComponent(schema)) {
 71 |       if (!schema['x-component-props']?.['dataIndex'] && !schema['name'])
 72 |         return []
 73 |       const name = schema['x-component-props']?.['dataIndex'] || schema['name']
 74 |       const field = arrayField.query(arrayField.address.concat(name)).take()
 75 |       const columnProps =
 76 |         field?.component?.[1] || schema['x-component-props'] || {}
 77 |       const display = field?.display || schema['x-display']
 78 |       return [
 79 |         {
 80 |           name,
 81 |           display,
 82 |           field,
 83 |           schema,
 84 |           columnProps: {
 85 |             title: field?.title || columnProps.title,
 86 |             ...columnProps,
 87 |           },
 88 |         },
 89 |       ]
 90 |     } else if (schema.properties) {
 91 |       return schema.reduceProperties((buf, schema) => {
 92 |         return buf.concat(parseSources(schema))
 93 |       }, [])
 94 |     }
 95 |   }
 96 | 
 97 |   const parseArrayItems = (schema: Schema['items']) => {
 98 |     if (!schema) return []
 99 |     const sources: ObservableColumnSource[] = []
100 |     const items = isArr(schema) ? schema : [schema]
101 |     return items.reduce((columns, schema) => {
102 |       const item = parseSources(schema)
103 |       if (item) {
104 |         return columns.concat(item)
105 |       }
106 |       return columns
107 |     }, sources)
108 |   }
109 | 
110 |   const validSchema = (
111 |     schema?.type === 'array' && schema?.items ? schema.items : schema
112 |   ) as Schema
113 | 
114 |   return parseArrayItems(validSchema)
115 | }
116 | 
117 | const useColumns = (
118 |   sources: ObservableColumnSource[]
119 | ): TableProps<any>['columns'] => {
120 |   return sources.reduce((buf, { name, columnProps, schema, display }, key) => {
121 |     if (display !== 'visible') return buf
122 |     if (!isColumnComponent(schema)) return buf
123 |     return buf.concat({
124 |       ...columnProps,
125 |       key,
126 |       dataIndex: name,
127 |     })
128 |   }, [])
129 | }
130 | 
131 | const addPrimaryKey = (dataSource, rowKey, primaryKey) =>
132 |   dataSource.map((item) => {
133 |     const children = isArr(item.children)
134 |       ? addPrimaryKey(item.children, rowKey, primaryKey)
135 |       : {}
136 |     return {
137 |       ...item,
138 |       ...children,
139 |       [primaryKey]: rowKey(item),
140 |     }
141 |   })
142 | 
143 | export const SelectTable: ComposedSelectTable = observer((props) => {
144 |   const {
145 |     mode = 'multiple',
146 |     dataSource: propsDataSource,
147 |     optionAsValue,
148 |     valueType = 'all',
149 |     showSearch = false,
150 |     filterOption,
151 |     filterSort,
152 |     onSearch,
153 |     searchProps,
154 |     className,
155 |     value,
156 |     onChange,
157 |     rowSelection,
158 |     primaryKey: rowKey = 'key',
159 |     ...otherTableProps
160 |   } = props
161 |   const prefixCls = usePrefixCls('formily-select-table', props)
162 |   const [searchValue, setSearchValue] = useState<string>()
163 |   const field = useField() as any
164 |   const loading = isBool(props.loading) ? props.loading : field.loading
165 |   const disabled = field.disabled
166 |   const readOnly = field.readOnly
167 |   const readPretty = field.readPretty
168 |   const { searchSize, tableSize } = useSize(
169 |     field.decoratorProps?.size,
170 |     searchProps?.size,
171 |     props?.size
172 |   )
173 |   const primaryKey = isFn(rowKey) ? '__formily_key__' : rowKey
174 |   const sources = useSources()
175 |   const columns = useColumns(sources)
176 | 
177 |   // dataSource
178 |   let dataSource = isArr(propsDataSource) ? propsDataSource : field.dataSource
179 |   dataSource = isFn(rowKey)
180 |     ? addPrimaryKey(dataSource, rowKey, primaryKey)
181 |     : dataSource
182 | 
183 |   // Filter dataSource By Search
184 |   const filteredDataSource = useFilterOptions(
185 |     dataSource,
186 |     searchValue,
187 |     filterOption,
188 |     rowSelection?.checkStrictly
189 |   )
190 | 
191 |   // Order dataSource By filterSort
192 |   const orderedFilteredDataSource = useMemo(() => {
193 |     if (!filterSort) {
194 |       return filteredDataSource
195 |     }
196 |     return [...filteredDataSource].sort((a, b) => filterSort(a, b))
197 |   }, [filteredDataSource, filterSort])
198 | 
199 |   const flatDataSource = useFlatOptions(dataSource)
200 |   const flatFilteredDataSource = useFlatOptions(filteredDataSource)
201 | 
202 |   // 分页或异步查询时,dataSource会丢失已选数据,配置optionAsValue则无法获取已选数据,需要进行合并
203 |   const getWholeDataSource = () => {
204 |     if (optionAsValue && mode === 'multiple' && value?.length) {
205 |       const map = new Map()
206 |       const arr = [...flatDataSource, ...value]
207 |       arr.forEach((item) => {
208 |         if (!map.has(item[primaryKey])) {
209 |           map.set(item[primaryKey], item)
210 |         }
211 |       })
212 |       return [...map.values()]
213 |     }
214 |     return flatDataSource
215 |   }
216 | 
217 |   // selected keys for Table UI
218 |   const selected = getUISelected(
219 |     value,
220 |     flatDataSource,
221 |     primaryKey,
222 |     valueType,
223 |     optionAsValue,
224 |     mode,
225 |     rowSelection?.checkStrictly,
226 |     rowKey
227 |   )
228 | 
229 |   // readPretty Value
230 |   const readPrettyDataSource = useFilterOptions(
231 |     orderedFilteredDataSource,
232 |     selected,
233 |     (value, item) => value.includes(item[primaryKey])
234 |   )
235 | 
236 |   const onInnerSearch = (searchText) => {
237 |     const formatted = (searchText || '').trim()
238 |     setSearchValue(searchText)
239 |     onSearch?.(formatted)
240 |   }
241 | 
242 |   const onInnerChange = (selectedRowKeys: any[]) => {
243 |     if (readOnly) {
244 |       return
245 |     }
246 |     // 筛选后onChange默认的records数据不完整,此处需使用完整数据过滤
247 |     const wholeRecords = getWholeDataSource().filter((item) =>
248 |       selectedRowKeys.includes(item?.[primaryKey])
249 |     )
250 |     const { outputValue, outputOptions } = getOutputData(
251 |       selectedRowKeys,
252 |       wholeRecords,
253 |       dataSource,
254 |       primaryKey,
255 |       valueType,
256 |       optionAsValue,
257 |       mode,
258 |       rowSelection?.checkStrictly
259 |     )
260 | 
261 |     onChange?.(outputValue, outputOptions)
262 |   }
263 | 
264 |   const onRowClick = (record) => {
265 |     if (readPretty || disabled || readOnly || record?.disabled) {
266 |       return
267 |     }
268 |     const selectedRowKey = record?.[primaryKey]
269 |     const isSelected = selected?.includes(selectedRowKey)
270 |     let selectedRowKeys = []
271 |     if (mode === 'single') {
272 |       selectedRowKeys = [selectedRowKey]
273 |     } else {
274 |       if (isSelected) {
275 |         selectedRowKeys = selected.filter((item) => item !== selectedRowKey)
276 |       } else {
277 |         selectedRowKeys = [...selected, selectedRowKey]
278 |       }
279 |     }
280 |     if (rowSelection?.checkStrictly !== false) {
281 |       onInnerChange(selectedRowKeys)
282 |     } else {
283 |       onSlacklyChange(selectedRowKeys)
284 |     }
285 |   }
286 | 
287 |   // TreeData SlacklyChange
288 |   const onSlacklyChange = (currentSelected: any[]) => {
289 |     let { selectedRowKeys } = useCheckSlackly(
290 |       currentSelected,
291 |       selected,
292 |       flatDataSource,
293 |       flatFilteredDataSource,
294 |       primaryKey,
295 |       rowSelection?.checkStrictly
296 |     )
297 |     onInnerChange(selectedRowKeys)
298 |   }
299 | 
300 |   // Table All Checkbox
301 |   const titleAddon = useTitleAddon(
302 |     selected,
303 |     flatDataSource,
304 |     flatFilteredDataSource,
305 |     primaryKey,
306 |     mode,
307 |     disabled,
308 |     readOnly,
309 |     rowSelection?.checkStrictly,
310 |     onInnerChange
311 |   )
312 | 
313 |   // Antd rowSelection type
314 |   const modeAsType: any = { multiple: 'checkbox', single: 'radio' }?.[mode]
315 | 
316 |   return (
317 |     <div className={prefixCls}>
318 |       {showSearch ? (
319 |         <Search
320 |           {...searchProps}
321 |           className={cls(`${prefixCls}-search`, searchProps?.className)}
322 |           style={{ width: '100%', ...searchProps?.style }}
323 |           onSearch={onInnerSearch}
324 |           onChange={(e) => onInnerSearch(e.target.value)}
325 |           disabled={disabled}
326 |           readOnly={readOnly}
327 |           size={searchSize}
328 |           loading={loading} // antd
329 |         />
330 |       ) : null}
331 |       <Table
332 |         {...otherTableProps}
333 |         className={cls(`${prefixCls}-table`, className)}
334 |         dataSource={
335 |           readPretty ? readPrettyDataSource : orderedFilteredDataSource
336 |         }
337 |         rowSelection={
338 |           readPretty
339 |             ? undefined
340 |             : ({
341 |                 ...rowSelection,
342 |                 ...titleAddon,
343 |                 getCheckboxProps: (record) => ({
344 |                   ...(rowSelection?.getCheckboxProps?.(record) as any),
345 |                   disabled: disabled || record?.disabled,
346 |                 }), // antd
347 |                 ...(rowSelection?.checkStrictly !== false
348 |                   ? {}
349 |                   : {
350 |                       renderCell: (checked, record, index, originNode) => {
351 |                         return React.cloneElement(
352 |                           originNode as React.ReactElement,
353 |                           {
354 |                             indeterminate: getIndeterminate(
355 |                               record,
356 |                               flatDataSource,
357 |                               selected,
358 |                               primaryKey
359 |                             ),
360 |                           }
361 |                         )
362 |                       },
363 |                     }),
364 |                 selectedRowKeys: selected,
365 |                 onChange:
366 |                   rowSelection?.checkStrictly !== false
367 |                     ? onInnerChange
368 |                     : onSlacklyChange,
369 |                 type: modeAsType,
370 |                 preserveSelectedRowKeys: true,
371 |                 checkStrictly: true,
372 |               } as any)
373 |         }
374 |         columns={props.columns || columns}
375 |         rowKey={primaryKey}
376 |         loading={loading}
377 |         size={tableSize}
378 |         onRow={(record) => {
379 |           // antd
380 |           const onRowResult = otherTableProps.onRow?.(record)
381 |           return {
382 |             ...onRowResult,
383 |             onClick: (e) => {
384 |               onRowResult?.onClick?.(e)
385 |               onRowClick(record)
386 |             },
387 |           }
388 |         }}
389 |       >
390 |         {''}
391 |       </Table>
392 |       {sources.map((column, key) => {
393 |         //专门用来承接对Column的状态管理
394 |         if (!isColumnComponent(column.schema)) return
395 |         return React.createElement(RecursionField, {
396 |           name: column.name,
397 |           schema: column.schema,
398 |           onlyRenderSelf: true,
399 |           key,
400 |         })
401 |       })}
402 |     </div>
403 |   )
404 | })
405 | 
406 | const TableColumn: React.FC<
407 |   React.PropsWithChildren<ISelectTableColumnProps>
408 | > = () => <></>
409 | 
410 | SelectTable.Column = TableColumn
411 | 
412 | export default SelectTable
413 | 
```

--------------------------------------------------------------------------------
/packages/element/src/form-drawer/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, Drawer as DrawerProps } from 'element-ui'
 14 | import { Button, Drawer } 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 FormDrawerContentProps = { form: Form }
 29 | 
 30 | type FormDrawerContent = Component | ((props: FormDrawerContentProps) => VNode)
 31 | 
 32 | type DrawerTitle = string | number | Component | VNode | (() => VNode)
 33 | 
 34 | type IFormDrawerProps = Omit<DrawerProps, 'title'> & {
 35 |   title?: DrawerTitle
 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 = 'FormDrawerFooter'
 51 | 
 52 | const isDrawerTitle = (props: any): props is DrawerTitle => {
 53 |   return isNum(props) || isStr(props) || isBool(props) || isValidElement(props)
 54 | }
 55 | 
 56 | const getDrawerProps = (props: any): IFormDrawerProps => {
 57 |   if (isDrawerTitle(props)) {
 58 |     return {
 59 |       title: props,
 60 |     } as IFormDrawerProps
 61 |   } else {
 62 |     return props
 63 |   }
 64 | }
 65 | 
 66 | export interface IFormDrawer {
 67 |   forOpen(middleware: IMiddleware<IFormProps>): IFormDrawer
 68 |   forConfirm(middleware: IMiddleware<IFormProps>): IFormDrawer
 69 |   forCancel(middleware: IMiddleware<IFormProps>): IFormDrawer
 70 |   open(props?: IFormProps): Promise<any>
 71 |   close(): void
 72 | }
 73 | 
 74 | export interface IFormDrawerComponentProps {
 75 |   content: FormDrawerContent
 76 |   resolve: () => any
 77 |   reject: () => any
 78 | }
 79 | 
 80 | export function FormDrawer(
 81 |   title: IFormDrawerProps | DrawerTitle,
 82 |   content: FormDrawerContent
 83 | ): IFormDrawer
 84 | 
 85 | export function FormDrawer(
 86 |   title: IFormDrawerProps | DrawerTitle,
 87 |   id: string | symbol,
 88 |   content: FormDrawerContent
 89 | ): IFormDrawer
 90 | 
 91 | export function FormDrawer(
 92 |   title: DrawerTitle,
 93 |   id: string,
 94 |   content: FormDrawerContent
 95 | ): IFormDrawer
 96 | 
 97 | export function FormDrawer(
 98 |   title: IFormDrawerProps | DrawerTitle,
 99 |   id: string | symbol | FormDrawerContent,
100 |   content?: FormDrawerContent
101 | ): IFormDrawer {
102 |   if (isFn(id) || isValidElement(id)) {
103 |     content = id as FormDrawerContent
104 |     id = 'form-drawer'
105 |   }
106 | 
107 |   const prefixCls = `${stylePrefix}-form-drawer`
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 = getDrawerProps(title)
121 |   const drawerProps = {
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 = Vue.extend({
153 |         props: ['drawerProps'],
154 |         data() {
155 |           return {
156 |             visible: false,
157 |           }
158 |         },
159 |         render() {
160 |           const {
161 |             onClose,
162 |             onClosed,
163 |             onOpen,
164 |             onOpened,
165 |             onOK,
166 |             onCancel,
167 |             title,
168 |             footer,
169 |             okText,
170 |             cancelText,
171 |             okButtonProps,
172 |             cancelButtonProps,
173 |             ...drawerProps
174 |           } = this.drawerProps
175 | 
176 |           return h(
177 |             FormProvider,
178 |             {
179 |               props: {
180 |                 form: env.form,
181 |               },
182 |             },
183 |             {
184 |               default: () =>
185 |                 h(
186 |                   Drawer,
187 |                   {
188 |                     class: [`${prefixCls}`],
189 |                     attrs: {
190 |                       visible: this.visible,
191 |                       ...drawerProps,
192 |                     },
193 |                     on: {
194 |                       'update:visible': (val) => {
195 |                         this.visible = val
196 |                       },
197 |                       close: () => {
198 |                         onClose?.()
199 |                       },
200 | 
201 |                       closed: () => {
202 |                         onClosed?.()
203 |                       },
204 |                       open: () => {
205 |                         onOpen?.()
206 |                       },
207 |                       opened: () => {
208 |                         onOpened?.()
209 |                       },
210 |                     },
211 |                   },
212 |                   {
213 |                     default: () => [
214 |                       h(
215 |                         'div',
216 |                         {
217 |                           class: [`${prefixCls}-body`],
218 |                         },
219 |                         {
220 |                           default: () => h(component, {}, {}),
221 |                         }
222 |                       ),
223 |                       h(
224 |                         'div',
225 |                         {
226 |                           class: [`${prefixCls}-footer`],
227 |                         },
228 |                         {
229 |                           default: () => {
230 |                             const FooterProtalTarget = h(
231 |                               PortalTarget,
232 |                               {
233 |                                 props: {
234 |                                   name: PORTAL_TARGET_NAME,
235 |                                   slim: true,
236 |                                 },
237 |                               },
238 |                               {}
239 |                             )
240 | 
241 |                             if (footer === null) {
242 |                               return [null, FooterProtalTarget]
243 |                             } else if (footer) {
244 |                               return [
245 |                                 resolveComponent(footer),
246 |                                 FooterProtalTarget,
247 |                               ]
248 |                             }
249 | 
250 |                             return [
251 |                               h(
252 |                                 Button,
253 |                                 {
254 |                                   attrs: cancelButtonProps,
255 |                                   on: {
256 |                                     click: (e) => {
257 |                                       onCancel?.(e)
258 |                                       reject()
259 |                                     },
260 |                                   },
261 |                                 },
262 |                                 {
263 |                                   default: () =>
264 |                                     resolveComponent(
265 |                                       cancelText ||
266 |                                         t('el.popconfirm.cancelButtonText')
267 |                                     ),
268 |                                 }
269 |                               ),
270 | 
271 |                               h(
272 |                                 Button,
273 |                                 {
274 |                                   attrs: {
275 |                                     type: 'primary',
276 |                                     ...okButtonProps,
277 |                                   },
278 |                                   on: {
279 |                                     click: (e) => {
280 |                                       onOK?.(e)
281 |                                       resolve()
282 |                                     },
283 |                                   },
284 |                                 },
285 |                                 {
286 |                                   default: () =>
287 |                                     resolveComponent(
288 |                                       okText ||
289 |                                         t('el.popconfirm.confirmButtonText')
290 |                                     ),
291 |                                 }
292 |                               ),
293 |                               FooterProtalTarget,
294 |                             ]
295 |                           },
296 |                         }
297 |                       ),
298 |                     ],
299 |                     title: () =>
300 |                       h('div', {}, { default: () => resolveComponent(title) }),
301 |                   }
302 |                 ),
303 |             }
304 |           )
305 |         },
306 |       })
307 |       env.instance = new ComponentConstructor({
308 |         propsData: {
309 |           drawerProps,
310 |         },
311 |         parent: getProtalContext(id as string | symbol),
312 |       })
313 |       env.instance.$mount(env.root)
314 |     }
315 | 
316 |     env.instance.visible = visible
317 |   }
318 | 
319 |   const formDrawer = {
320 |     forOpen: (middleware: IMiddleware<IFormProps>) => {
321 |       if (isFn(middleware)) {
322 |         env.openMiddlewares.push(middleware)
323 |       }
324 |       return formDrawer
325 |     },
326 |     forConfirm: (middleware: IMiddleware<Form>) => {
327 |       if (isFn(middleware)) {
328 |         env.confirmMiddlewares.push(middleware)
329 |       }
330 |       return formDrawer
331 |     },
332 |     forCancel: (middleware: IMiddleware<Form>) => {
333 |       if (isFn(middleware)) {
334 |         env.cancelMiddlewares.push(middleware)
335 |       }
336 |       return formDrawer
337 |     },
338 |     open: (props: IFormProps) => {
339 |       if (env.promise) return env.promise
340 | 
341 |       env.promise = new Promise(async (resolve, reject) => {
342 |         try {
343 |           props = await loading(drawerProps.loadingText, () =>
344 |             applyMiddleware(props, env.openMiddlewares)
345 |           )
346 |           env.form = env.form || createForm(props)
347 |         } catch (e) {
348 |           reject(e)
349 |         }
350 | 
351 |         render(
352 |           true,
353 |           () => {
354 |             env.form
355 |               .submit(async () => {
356 |                 await applyMiddleware(env.form, env.confirmMiddlewares)
357 |                 resolve(toJS(env.form.values))
358 |                 if (drawerProps.beforeClose) {
359 |                   setTimeout(() => {
360 |                     drawerProps.beforeClose(() => {
361 |                       formDrawer.close()
362 |                     })
363 |                   })
364 |                 } else {
365 |                   formDrawer.close()
366 |                 }
367 |               })
368 |               .catch(() => {})
369 |           },
370 |           async () => {
371 |             await loading(drawerProps.loadingText, () =>
372 |               applyMiddleware(env.form, env.cancelMiddlewares)
373 |             )
374 | 
375 |             if (drawerProps.beforeClose) {
376 |               drawerProps.beforeClose(() => {
377 |                 formDrawer.close()
378 |               })
379 |             } else {
380 |               formDrawer.close()
381 |             }
382 |           }
383 |         )
384 |       })
385 |       return env.promise
386 |     },
387 |     close: () => {
388 |       if (!env.root) return
389 |       render(false)
390 |     },
391 |   }
392 |   return formDrawer
393 | }
394 | 
395 | const FormDrawerFooter = defineComponent({
396 |   name: 'FFormDrawerFooter',
397 |   setup(props, { slots }) {
398 |     return () => {
399 |       return h(
400 |         Portal,
401 |         {
402 |           props: {
403 |             to: PORTAL_TARGET_NAME,
404 |           },
405 |         },
406 |         slots
407 |       )
408 |     }
409 |   },
410 | })
411 | 
412 | FormDrawer.Footer = FormDrawerFooter
413 | FormDrawer.Protal = createPortalProvider('form-drawer')
414 | 
415 | export default FormDrawer
416 | 
```

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

```markdown
  1 | # Select
  2 | 
  3 | > 下拉框组件
  4 | 
  5 | ## Markup Schema 同步数据源案例
  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="选择框"
 28 |         x-decorator="FormItem"
 29 |         x-component="Select"
 30 |         enum={[
 31 |           { label: '选项1', value: 1 },
 32 |           { label: '选项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>
 43 |     </FormButtonGroup>
 44 |   </FormProvider>
 45 | )
 46 | ```
 47 | 
 48 | ## Markup Schema 异步搜索案例
 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="异步搜索选择框"
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>
164 |     </FormButtonGroup>
165 |   </FormProvider>
166 | )
167 | ```
168 | 
169 | ## Markup Schema 异步联动数据源案例
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="联动选择框"
242 |         x-decorator="FormItem"
243 |         x-component="Select"
244 |         enum={[
245 |           { label: '发请求1', value: 1 },
246 |           { label: '发请求2', value: 2 },
247 |         ]}
248 |         x-component-props={{
249 |           style: {
250 |             width: 120,
251 |           },
252 |         }}
253 |       />
254 |       <SchemaField.String
255 |         name="select"
256 |         title="异步选择框"
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>
268 |     </FormButtonGroup>
269 |   </FormProvider>
270 | )
271 | ```
272 | 
273 | ## JSON Schema 同步数据源案例
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: '选择框',
296 |       'x-decorator': 'FormItem',
297 |       'x-component': 'Select',
298 |       enum: [
299 |         { label: '选项1', value: 1 },
300 |         { label: '选项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>
316 |     </FormButtonGroup>
317 |   </FormProvider>
318 | )
319 | ```
320 | 
321 | ## JSON Schema 异步联动数据源案例
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: '联动选择框',
387 |       enum: [
388 |         { label: '发请求1', value: 1 },
389 |         { label: '发请求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: '异步选择框',
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>
419 |     </FormButtonGroup>
420 |   </FormProvider>
421 | )
422 | ```
423 | 
424 | ## 纯 JSX 同步数据源案例
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="选择框"
439 |       dataSource={[
440 |         { label: '选项1', value: 1 },
441 |         { label: '选项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>
455 |     </FormButtonGroup>
456 |   </FormProvider>
457 | )
458 | ```
459 | 
460 | ## 纯 JSX 异步联动数据源案例
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="联动选择框"
530 |       dataSource={[
531 |         { label: '发请求1', value: 1 },
532 |         { label: '发请求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="异步选择框"
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>
559 |     </FormButtonGroup>
560 |   </FormProvider>
561 | )
562 | ```
563 | 
564 | ## API
565 | 
566 | 参考 https://ant.design/components/select-cn/
567 | 
```
Page 27/52FirstPrevNextLast