This is page 34 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/path/src/__tests__/parser.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import expect from 'expect' 2 | import { Parser } from '../parser' 3 | import { Path } from '../' 4 | const parse = (string: string, json: any, index: number) => { 5 | test('test ' + string + ` : ${index}`, () => { 6 | const parser = new Parser(string) 7 | expect(parser.parse()).toEqual(json) 8 | }) 9 | } 10 | 11 | const batchTest = (obj) => { 12 | let i = 0 13 | for (let key in obj) { 14 | i++ 15 | parse(key, obj[key], i) 16 | } 17 | } 18 | 19 | test('relative', () => { 20 | const parser = new Parser('..[ + 1 ].dd.bb', new Path(['aa', '1', 'cc'])) 21 | const parser2 = new Parser('.ee', new Path(['aa', '1', 'cc'])) 22 | const parser3 = new Parser('.', new Path(['aa', '1', 'cc'])) 23 | const parser4 = new Parser('..', new Path(['aa', '1', 'cc'])) 24 | const parser5 = new Parser('.[].dd', new Path(['aa', '1'])) 25 | const parser6 = new Parser('.[].[]', new Path(['aa', '1'])) 26 | const parser7 = new Parser('.[].[aa]', new Path(['aa', '1'])) 27 | parser2.parse() 28 | parser3.parse() 29 | parser4.parse() 30 | parser5.parse() 31 | parser6.parse() 32 | parser7.parse() 33 | expect(parser.parse()).toEqual({ 34 | type: 'Identifier', 35 | value: 'aa', 36 | after: { 37 | type: 'DotOperator', 38 | after: { 39 | type: 'DestructorExpression', 40 | value: { 41 | type: 'ArrayPattern', 42 | elements: [ 43 | { 44 | type: 'Identifier', 45 | value: '+', 46 | after: { 47 | type: 'Identifier', 48 | value: '1', 49 | }, 50 | }, 51 | ], 52 | }, 53 | source: '2', 54 | after: { 55 | type: 'DotOperator', 56 | after: { 57 | type: 'Identifier', 58 | value: 'dd', 59 | after: { 60 | type: 'DotOperator', 61 | after: { 62 | type: 'Identifier', 63 | value: 'bb', 64 | }, 65 | }, 66 | }, 67 | }, 68 | }, 69 | }, 70 | }) 71 | expect(parser.data.segments).toEqual(['aa', '2', 'dd', 'bb']) 72 | expect(parser2.data.segments).toEqual(['aa', '1', 'ee']) 73 | expect(parser3.data.segments).toEqual(['aa', '1']) 74 | expect(parser4.data.segments).toEqual(['aa']) 75 | expect(parser5.data.segments).toEqual(['aa', '1', 'dd']) 76 | expect(parser6.data.segments).toEqual(['aa', '1', '[]']) 77 | expect(parser7.data.segments).toEqual(['aa', '1', '[aa]']) 78 | }) 79 | 80 | test('calculate', () => { 81 | const parser0 = new Parser('..[+].dd.bb', new Path(['aa', '1', 'cc'])) 82 | parser0.parse() 83 | expect(parser0.data.segments).toEqual(['aa', '2', 'dd', 'bb']) 84 | 85 | const parser = new Parser('..[ -1 ].dd.bb', new Path(['aa', '1', 'cc'])) 86 | parser.parse() 87 | expect(parser.data.segments).toEqual(['aa', '0', 'dd', 'bb']) 88 | 89 | // TODO support 90 | // const parser2 = new Parser('..[ *2 ].dd.bb', new Path(['aa', '2', 'cc'])) 91 | // parser2.parse() 92 | // expect(parser2.data.segments).toEqual(['aa', '4', 'dd', 'bb']) 93 | 94 | const parser3 = new Parser('..[ /2 ].dd.bb', new Path(['aa', '2', 'cc'])) 95 | parser3.parse() 96 | expect(parser3.data.segments).toEqual(['aa', '1', 'dd', 'bb']) 97 | 98 | const parser4 = new Parser('..[ +1 ].dd.bb', new Path(['aa', 'a', 'cc'])) 99 | parser4.parse() 100 | expect(parser4.data.segments).toEqual(['aa', 'a1', 'dd', 'bb']) 101 | 102 | const parser5 = new Parser('..[ -1 ].dd.bb', new Path(['aa', 'a', 'cc'])) 103 | parser5.parse() 104 | expect(parser5.data.segments).toEqual(['aa', 'NaN', 'dd', 'bb']) 105 | 106 | // TODO support 107 | // const parser6 = new Parser('..[ *1 ].dd.bb', new Path(['aa', 'a', 'cc'])) 108 | // parser6.parse() 109 | // expect(parser6.data.segments).toEqual(['aa', 'NaN', 'dd', 'bb']) 110 | 111 | const parser7 = new Parser('..[ /1 ].dd.bb', new Path(['aa', 'a', 'cc'])) 112 | parser7.parse() 113 | expect(parser7.data.segments).toEqual(['aa', 'NaN', 'dd', 'bb']) 114 | 115 | const parser8 = new Parser('..[1].dd.bb', new Path(['aa', '1', 'cc'])) 116 | parser8.parse() 117 | expect(parser8.data.segments).toEqual(['aa', '2', 'dd', 'bb']) 118 | const parser9 = new Parser('') 119 | parser9.next() 120 | expect(parser9.parseObjectProperties()).toEqual([]) 121 | }) 122 | 123 | test('parser unexpected', () => { 124 | const parser = new Parser('array[]') 125 | expect(() => parser.parse()).toThrowError() 126 | 127 | const parser2 = new Parser('array[0.') 128 | expect(() => parser2.parse()).toThrowError() 129 | 130 | const parser3 = new Parser('.[+]', new Path('*.1.cc')) 131 | expect(() => parser3.parse()).toThrowError() 132 | 133 | const parser4 = new Parser('[:,4]') 134 | expect(() => parser4.parse()).toThrowError() 135 | }) 136 | 137 | test('tokenizer', () => { 138 | const originFromCharCode = String.fromCharCode 139 | String.fromCharCode = null 140 | 141 | const parser = new Parser('aa.bb') 142 | parser.parse() 143 | expect(parser.data.segments).toEqual(['aa', 'bb']) 144 | 145 | const parser2 = new Parser('array.0.[aa,bb]') 146 | parser2.parse() 147 | expect(parser2.data.segments).toEqual(['array', '0', '[aa,bb]']) 148 | 149 | String.fromCharCode = originFromCharCode 150 | 151 | const char13 = String.fromCharCode(13) 152 | const parser3 = new Parser(`${char13} aa.bb`) 153 | parser3.parse() 154 | 155 | const char11 = String.fromCharCode(11) 156 | const parser4 = new Parser(`${char11} aa.bb`) 157 | parser4.parse() 158 | 159 | const parser5 = new Parser('') 160 | parser5.next() 161 | parser5.parse() 162 | expect(parser5.data.segments).toEqual([]) 163 | 164 | const char10 = String.fromCharCode(10) 165 | const parser6 = new Parser(`{ 166 | c${char13}${char10}: kk, 167 | d : mm 168 | }`) 169 | parser6.parse() 170 | expect(parser6.data.segments).toEqual(['{c:kk,d:mm}']) 171 | 172 | const parser7 = new Parser(`{ 173 | c${char13}${char11}: kk, 174 | d : mm 175 | }`) 176 | parser7.parse() 177 | expect(parser7.data.segments).toEqual(['{c:kk,d:mm}']) 178 | 179 | const parser8 = new Parser(`\\name`) 180 | parser8.state.pos++ 181 | parser8.parse() 182 | expect(parser8.data.segments).toEqual(['name']) 183 | 184 | const parser9 = new Parser(`[a,{b}]`) 185 | parser9.parse() 186 | expect(parser9.data.segments).toEqual(['[a,{b}]']) 187 | 188 | const parser10 = new Parser(`*(a.b.c.*(aa,bb))`) 189 | parser10.parse() 190 | expect(parser10.data.segments).toEqual([]) 191 | 192 | const parser11 = new Parser(`{a,[b,{c}]. }`) 193 | expect(() => parser11.parse()).toThrowError() 194 | 195 | const parser12 = new Parser(`*(a.*[1:3])`) 196 | parser12.parse() 197 | expect(parser12.data.segments).toEqual([]) 198 | 199 | const parser13 = new Parser(`*(a.*[1:3]])`) 200 | expect(() => parser13.parse()).toThrowError() 201 | }) 202 | 203 | batchTest({ 204 | '*': { 205 | type: 'WildcardOperator', 206 | }, 207 | 'a.b.c': { 208 | type: 'Identifier', 209 | value: 'a', 210 | after: { 211 | type: 'DotOperator', 212 | after: { 213 | type: 'Identifier', 214 | value: 'b', 215 | after: { 216 | type: 'DotOperator', 217 | after: { 218 | type: 'Identifier', 219 | value: 'c', 220 | }, 221 | }, 222 | }, 223 | }, 224 | }, 225 | 'a.b.*': { 226 | type: 'Identifier', 227 | value: 'a', 228 | after: { 229 | type: 'DotOperator', 230 | after: { 231 | type: 'Identifier', 232 | value: 'b', 233 | after: { 234 | type: 'DotOperator', 235 | after: { 236 | type: 'WildcardOperator', 237 | }, 238 | }, 239 | }, 240 | }, 241 | }, 242 | 'a.b.*(111,222,aaa)': { 243 | type: 'Identifier', 244 | value: 'a', 245 | after: { 246 | type: 'DotOperator', 247 | after: { 248 | type: 'Identifier', 249 | value: 'b', 250 | after: { 251 | type: 'DotOperator', 252 | after: { 253 | type: 'WildcardOperator', 254 | filter: { 255 | type: 'GroupExpression', 256 | value: [ 257 | { 258 | type: 'Identifier', 259 | value: '111', 260 | }, 261 | { 262 | type: 'Identifier', 263 | value: '222', 264 | }, 265 | { 266 | type: 'Identifier', 267 | value: 'aaa', 268 | }, 269 | ], 270 | }, 271 | }, 272 | }, 273 | }, 274 | }, 275 | }, 276 | 'a.b.*(!111,222,aaa)': { 277 | type: 'Identifier', 278 | value: 'a', 279 | after: { 280 | type: 'DotOperator', 281 | after: { 282 | type: 'Identifier', 283 | value: 'b', 284 | after: { 285 | type: 'DotOperator', 286 | after: { 287 | type: 'WildcardOperator', 288 | filter: { 289 | type: 'GroupExpression', 290 | isExclude: true, 291 | value: [ 292 | { 293 | type: 'Identifier', 294 | value: '111', 295 | }, 296 | { 297 | type: 'Identifier', 298 | value: '222', 299 | }, 300 | { 301 | type: 'Identifier', 302 | value: 'aaa', 303 | }, 304 | ], 305 | }, 306 | }, 307 | }, 308 | }, 309 | }, 310 | }, 311 | 'a.b. * [ 11 : 22 ]': { 312 | type: 'Identifier', 313 | value: 'a', 314 | after: { 315 | type: 'DotOperator', 316 | after: { 317 | type: 'Identifier', 318 | value: 'b', 319 | after: { 320 | type: 'DotOperator', 321 | after: { 322 | type: 'WildcardOperator', 323 | filter: { 324 | type: 'RangeExpression', 325 | start: { 326 | type: 'Identifier', 327 | value: '11', 328 | }, 329 | end: { 330 | type: 'Identifier', 331 | value: '22', 332 | }, 333 | }, 334 | }, 335 | }, 336 | }, 337 | }, 338 | }, 339 | 'a.b.*([[123123!,()]],[[aaa]])': { 340 | type: 'Identifier', 341 | value: 'a', 342 | after: { 343 | type: 'DotOperator', 344 | after: { 345 | type: 'Identifier', 346 | value: 'b', 347 | after: { 348 | type: 'DotOperator', 349 | after: { 350 | type: 'WildcardOperator', 351 | filter: { 352 | type: 'GroupExpression', 353 | value: [ 354 | { 355 | type: 'IgnoreExpression', 356 | value: '123123!,()', 357 | }, 358 | { 359 | type: 'IgnoreExpression', 360 | value: 'aaa', 361 | }, 362 | ], 363 | }, 364 | }, 365 | }, 366 | }, 367 | }, 368 | }, 369 | 'a.b.*([[123123!,()]],aaa)': { 370 | type: 'Identifier', 371 | value: 'a', 372 | after: { 373 | type: 'DotOperator', 374 | after: { 375 | type: 'Identifier', 376 | value: 'b', 377 | after: { 378 | type: 'DotOperator', 379 | after: { 380 | type: 'WildcardOperator', 381 | filter: { 382 | type: 'GroupExpression', 383 | value: [ 384 | { 385 | type: 'IgnoreExpression', 386 | value: '123123!,()', 387 | }, 388 | { 389 | type: 'Identifier', 390 | value: 'aaa', 391 | }, 392 | ], 393 | }, 394 | }, 395 | }, 396 | }, 397 | }, 398 | }, 399 | 'a.b.*(![[123123!,()]],aaa)': { 400 | type: 'Identifier', 401 | value: 'a', 402 | after: { 403 | type: 'DotOperator', 404 | after: { 405 | type: 'Identifier', 406 | value: 'b', 407 | after: { 408 | type: 'DotOperator', 409 | after: { 410 | type: 'WildcardOperator', 411 | filter: { 412 | type: 'GroupExpression', 413 | value: [ 414 | { 415 | type: 'IgnoreExpression', 416 | value: '123123!,()', 417 | }, 418 | { 419 | type: 'Identifier', 420 | value: 'aaa', 421 | }, 422 | ], 423 | isExclude: true, 424 | }, 425 | }, 426 | }, 427 | }, 428 | }, 429 | }, 430 | 'a.b . * (![[123123!,()]],aaa,bbb)': { 431 | type: 'Identifier', 432 | value: 'a', 433 | after: { 434 | type: 'DotOperator', 435 | after: { 436 | type: 'Identifier', 437 | value: 'b', 438 | after: { 439 | type: 'DotOperator', 440 | after: { 441 | type: 'WildcardOperator', 442 | filter: { 443 | type: 'GroupExpression', 444 | value: [ 445 | { 446 | type: 'IgnoreExpression', 447 | value: '123123!,()', 448 | }, 449 | { 450 | type: 'Identifier', 451 | value: 'aaa', 452 | }, 453 | { 454 | type: 'Identifier', 455 | value: 'bbb', 456 | }, 457 | ], 458 | isExclude: true, 459 | }, 460 | }, 461 | }, 462 | }, 463 | }, 464 | }, 465 | 'a.b.[[123123!,()]] ': { 466 | type: 'Identifier', 467 | value: 'a', 468 | after: { 469 | type: 'DotOperator', 470 | after: { 471 | type: 'Identifier', 472 | value: 'b', 473 | after: { 474 | type: 'DotOperator', 475 | after: { 476 | type: 'IgnoreExpression', 477 | value: '123123!,()', 478 | }, 479 | }, 480 | }, 481 | }, 482 | }, 483 | [`a . 484 | b . 485 | [[123123!,()]] 486 | 487 | .aaaa`]: { 488 | type: 'Identifier', 489 | value: 'a', 490 | after: { 491 | type: 'DotOperator', 492 | after: { 493 | type: 'Identifier', 494 | value: 'b', 495 | after: { 496 | type: 'DotOperator', 497 | after: { 498 | type: 'IgnoreExpression', 499 | value: '123123!,()', 500 | after: { 501 | type: 'DotOperator', 502 | after: { 503 | type: 'Identifier', 504 | value: 'aaaa', 505 | }, 506 | }, 507 | }, 508 | }, 509 | }, 510 | }, 511 | }, 512 | 'a.*(aaa.d.*(!sss),ddd,bbb).c.b': { 513 | type: 'Identifier', 514 | value: 'a', 515 | after: { 516 | type: 'DotOperator', 517 | after: { 518 | type: 'WildcardOperator', 519 | filter: { 520 | type: 'GroupExpression', 521 | value: [ 522 | { 523 | type: 'Identifier', 524 | value: 'aaa', 525 | after: { 526 | type: 'DotOperator', 527 | after: { 528 | type: 'Identifier', 529 | value: 'd', 530 | after: { 531 | type: 'DotOperator', 532 | after: { 533 | type: 'WildcardOperator', 534 | filter: { 535 | type: 'GroupExpression', 536 | isExclude: true, 537 | value: [ 538 | { 539 | type: 'Identifier', 540 | value: 'sss', 541 | }, 542 | ], 543 | }, 544 | }, 545 | }, 546 | }, 547 | }, 548 | }, 549 | { 550 | type: 'Identifier', 551 | value: 'ddd', 552 | }, 553 | { 554 | type: 'Identifier', 555 | value: 'bbb', 556 | }, 557 | ], 558 | }, 559 | after: { 560 | type: 'DotOperator', 561 | after: { 562 | type: 'Identifier', 563 | value: 'c', 564 | after: { 565 | type: 'DotOperator', 566 | after: { 567 | type: 'Identifier', 568 | value: 'b', 569 | }, 570 | }, 571 | }, 572 | }, 573 | }, 574 | }, 575 | }, 576 | 'aa.bb.cc.{aa,bb,cc:kk}': { 577 | type: 'Identifier', 578 | value: 'aa', 579 | after: { 580 | type: 'DotOperator', 581 | after: { 582 | type: 'Identifier', 583 | value: 'bb', 584 | after: { 585 | type: 'DotOperator', 586 | after: { 587 | type: 'Identifier', 588 | value: 'cc', 589 | after: { 590 | type: 'DotOperator', 591 | after: { 592 | type: 'DestructorExpression', 593 | value: { 594 | type: 'ObjectPattern', 595 | properties: [ 596 | { 597 | type: 'ObjectPatternProperty', 598 | key: { type: 'Identifier', value: 'aa' }, 599 | }, 600 | { 601 | type: 'ObjectPatternProperty', 602 | key: { type: 'Identifier', value: 'bb' }, 603 | }, 604 | { 605 | type: 'ObjectPatternProperty', 606 | key: { type: 'Identifier', value: 'cc' }, 607 | value: { type: 'Identifier', value: 'kk' }, 608 | }, 609 | ], 610 | }, 611 | source: '{aa,bb,cc:kk}', 612 | }, 613 | }, 614 | }, 615 | }, 616 | }, 617 | }, 618 | }, 619 | 'aa.bb.cc.[ [aa,bb,cc,[ [{aa:bb}] ]] ]': { 620 | type: 'Identifier', 621 | value: 'aa', 622 | after: { 623 | type: 'DotOperator', 624 | after: { 625 | type: 'Identifier', 626 | value: 'bb', 627 | after: { 628 | type: 'DotOperator', 629 | after: { 630 | type: 'Identifier', 631 | value: 'cc', 632 | after: { 633 | type: 'DotOperator', 634 | after: { 635 | type: 'DestructorExpression', 636 | value: { 637 | type: 'ArrayPattern', 638 | elements: [ 639 | { 640 | type: 'ArrayPattern', 641 | elements: [ 642 | { 643 | type: 'Identifier', 644 | value: 'aa', 645 | }, 646 | { 647 | type: 'Identifier', 648 | value: 'bb', 649 | }, 650 | { 651 | type: 'Identifier', 652 | value: 'cc', 653 | }, 654 | { 655 | type: 'ArrayPattern', 656 | elements: [ 657 | { 658 | type: 'ArrayPattern', 659 | elements: [ 660 | { 661 | type: 'ObjectPattern', 662 | properties: [ 663 | { 664 | type: 'ObjectPatternProperty', 665 | key: { 666 | type: 'Identifier', 667 | value: 'aa', 668 | }, 669 | value: { 670 | type: 'Identifier', 671 | value: 'bb', 672 | }, 673 | }, 674 | ], 675 | }, 676 | ], 677 | }, 678 | ], 679 | }, 680 | ], 681 | }, 682 | ], 683 | }, 684 | source: '[[aa,bb,cc,[[{aa:bb}]]]]', 685 | }, 686 | }, 687 | }, 688 | }, 689 | }, 690 | }, 691 | }, 692 | }) 693 | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/TreeSelect.zh-CN.md: -------------------------------------------------------------------------------- ```markdown 1 | # TreeSelect 2 | 3 | > 树选择器 4 | 5 | ## Markup Schema 同步数据源案例 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/next' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | TreeSelect, 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 | label="选择框" 28 | x-decorator="FormItem" 29 | x-component="TreeSelect" 30 | enum={[ 31 | { 32 | label: '选项1', 33 | value: 1, 34 | children: [ 35 | { 36 | label: 'Child Node1', 37 | value: '0-0-0', 38 | key: '0-0-0', 39 | }, 40 | { 41 | label: 'Child Node2', 42 | value: '0-0-1', 43 | key: '0-0-1', 44 | }, 45 | { 46 | label: 'Child Node3', 47 | value: '0-0-2', 48 | key: '0-0-2', 49 | }, 50 | ], 51 | }, 52 | { 53 | label: '选项2', 54 | value: 2, 55 | children: [ 56 | { 57 | label: 'Child Node3', 58 | value: '0-1-0', 59 | key: '0-1-0', 60 | }, 61 | { 62 | label: 'Child Node4', 63 | value: '0-1-1', 64 | key: '0-1-1', 65 | }, 66 | { 67 | label: 'Child Node5', 68 | value: '0-1-2', 69 | key: '0-1-2', 70 | }, 71 | ], 72 | }, 73 | ]} 74 | x-component-props={{ 75 | style: { 76 | width: 200, 77 | }, 78 | }} 79 | /> 80 | </SchemaField> 81 | <FormButtonGroup> 82 | <Submit onSubmit={console.log}>提交</Submit> 83 | </FormButtonGroup> 84 | </FormProvider> 85 | ) 86 | ``` 87 | 88 | ## Markup Schema 异步联动数据源案例 89 | 90 | ```tsx 91 | import React from 'react' 92 | import { 93 | TreeSelect, 94 | Select, 95 | FormItem, 96 | FormButtonGroup, 97 | Submit, 98 | } from '@formily/next' 99 | import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core' 100 | import { FormProvider, createSchemaField } from '@formily/react' 101 | import { action } from '@formily/reactive' 102 | 103 | const SchemaField = createSchemaField({ 104 | components: { 105 | Select, 106 | TreeSelect, 107 | FormItem, 108 | }, 109 | }) 110 | 111 | const useAsyncDataSource = ( 112 | pattern: FormPathPattern, 113 | service: (field: Field) => Promise<{ label: string; value: any }[]> 114 | ) => { 115 | onFieldReact(pattern, (field) => { 116 | field.loading = true 117 | service(field).then( 118 | action.bound((data) => { 119 | field.dataSource = data 120 | field.loading = false 121 | }) 122 | ) 123 | }) 124 | } 125 | 126 | const form = createForm({ 127 | effects: () => { 128 | useAsyncDataSource('select', async (field) => { 129 | const linkage = field.query('linkage').get('value') 130 | if (!linkage) return [] 131 | return new Promise((resolve) => { 132 | setTimeout(() => { 133 | if (linkage === 1) { 134 | resolve([ 135 | { 136 | label: 'AAA', 137 | value: 'aaa', 138 | children: [ 139 | { 140 | label: 'Child Node1', 141 | value: '0-0-0', 142 | key: '0-0-0', 143 | }, 144 | { 145 | label: 'Child Node2', 146 | value: '0-0-1', 147 | key: '0-0-1', 148 | }, 149 | { 150 | label: 'Child Node3', 151 | value: '0-0-2', 152 | key: '0-0-2', 153 | }, 154 | ], 155 | }, 156 | { 157 | label: 'BBB', 158 | value: 'ccc', 159 | children: [ 160 | { 161 | label: 'Child Node1', 162 | value: '0-1-0', 163 | key: '0-1-0', 164 | }, 165 | { 166 | label: 'Child Node2', 167 | value: '0-1-1', 168 | key: '0-1-1', 169 | }, 170 | { 171 | label: 'Child Node3', 172 | value: '0-1-2', 173 | key: '0-1-2', 174 | }, 175 | ], 176 | }, 177 | ]) 178 | } else if (linkage === 2) { 179 | resolve([ 180 | { 181 | label: 'CCC', 182 | value: 'ccc', 183 | children: [ 184 | { 185 | label: 'Child Node1', 186 | value: '0-0-0', 187 | key: '0-0-0', 188 | }, 189 | { 190 | label: 'Child Node2', 191 | value: '0-0-1', 192 | key: '0-0-1', 193 | }, 194 | { 195 | label: 'Child Node3', 196 | value: '0-0-2', 197 | key: '0-0-2', 198 | }, 199 | ], 200 | }, 201 | { 202 | label: 'DDD', 203 | value: 'ddd', 204 | children: [ 205 | { 206 | label: 'Child Node1', 207 | value: '0-1-0', 208 | key: '0-1-0', 209 | }, 210 | { 211 | label: 'Child Node2', 212 | value: '0-1-1', 213 | key: '0-1-1', 214 | }, 215 | { 216 | label: 'Child Node3', 217 | value: '0-1-2', 218 | key: '0-1-2', 219 | }, 220 | ], 221 | }, 222 | ]) 223 | } 224 | }, 1500) 225 | }) 226 | }) 227 | }, 228 | }) 229 | 230 | export default () => ( 231 | <FormProvider form={form}> 232 | <SchemaField> 233 | <SchemaField.Number 234 | name="linkage" 235 | label="联动选择框" 236 | x-decorator="FormItem" 237 | x-component="Select" 238 | enum={[ 239 | { label: '发请求1', value: 1 }, 240 | { label: '发请求2', value: 2 }, 241 | ]} 242 | x-component-props={{ 243 | style: { 244 | width: 200, 245 | }, 246 | }} 247 | /> 248 | <SchemaField.String 249 | name="select" 250 | label="异步选择框" 251 | x-decorator="FormItem" 252 | x-component="TreeSelect" 253 | x-component-props={{ 254 | style: { 255 | width: 200, 256 | }, 257 | }} 258 | /> 259 | </SchemaField> 260 | <FormButtonGroup> 261 | <Submit onSubmit={console.log}>提交</Submit> 262 | </FormButtonGroup> 263 | </FormProvider> 264 | ) 265 | ``` 266 | 267 | ## JSON Schema 同步数据源案例 268 | 269 | ```tsx 270 | import React from 'react' 271 | import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/next' 272 | import { createForm } from '@formily/core' 273 | import { FormProvider, createSchemaField } from '@formily/react' 274 | 275 | const SchemaField = createSchemaField({ 276 | components: { 277 | TreeSelect, 278 | FormItem, 279 | }, 280 | }) 281 | 282 | const form = createForm() 283 | 284 | const schema = { 285 | type: 'object', 286 | properties: { 287 | select: { 288 | type: 'string', 289 | label: '选择框', 290 | 'x-decorator': 'FormItem', 291 | 'x-component': 'TreeSelect', 292 | enum: [ 293 | { 294 | label: '选项1', 295 | value: 1, 296 | children: [ 297 | { 298 | label: 'Child Node1', 299 | value: '0-0-0', 300 | key: '0-0-0', 301 | }, 302 | { 303 | label: 'Child Node2', 304 | value: '0-0-1', 305 | key: '0-0-1', 306 | }, 307 | { 308 | label: 'Child Node3', 309 | value: '0-0-2', 310 | key: '0-0-2', 311 | }, 312 | ], 313 | }, 314 | { 315 | label: '选项2', 316 | value: 2, 317 | children: [ 318 | { 319 | label: 'Child Node1', 320 | value: '0-1-0', 321 | key: '0-1-0', 322 | }, 323 | { 324 | label: 'Child Node2', 325 | value: '0-1-1', 326 | key: '0-1-1', 327 | }, 328 | { 329 | label: 'Child Node3', 330 | value: '0-1-2', 331 | key: '0-1-2', 332 | }, 333 | ], 334 | }, 335 | ], 336 | 'x-component-props': { 337 | style: { 338 | width: 200, 339 | }, 340 | }, 341 | }, 342 | }, 343 | } 344 | 345 | export default () => ( 346 | <FormProvider form={form}> 347 | <SchemaField schema={schema} /> 348 | <FormButtonGroup> 349 | <Submit onSubmit={console.log}>提交</Submit> 350 | </FormButtonGroup> 351 | </FormProvider> 352 | ) 353 | ``` 354 | 355 | ## JSON Schema 异步联动数据源案例 356 | 357 | ```tsx 358 | import React from 'react' 359 | import { 360 | TreeSelect, 361 | Select, 362 | FormItem, 363 | FormButtonGroup, 364 | Submit, 365 | } from '@formily/next' 366 | import { createForm } from '@formily/core' 367 | import { FormProvider, createSchemaField } from '@formily/react' 368 | import { action } from '@formily/reactive' 369 | 370 | const SchemaField = createSchemaField({ 371 | components: { 372 | Select, 373 | TreeSelect, 374 | FormItem, 375 | }, 376 | }) 377 | 378 | const loadData = async (field) => { 379 | const linkage = field.query('linkage').get('value') 380 | if (!linkage) return [] 381 | return new Promise((resolve) => { 382 | setTimeout(() => { 383 | if (linkage === 1) { 384 | resolve([ 385 | { 386 | label: 'AAA', 387 | value: 'aaa', 388 | children: [ 389 | { 390 | label: 'Child Node1', 391 | value: '0-0-0', 392 | key: '0-0-0', 393 | }, 394 | { 395 | label: 'Child Node2', 396 | value: '0-0-1', 397 | key: '0-0-1', 398 | }, 399 | { 400 | label: 'Child Node3', 401 | value: '0-0-2', 402 | key: '0-0-2', 403 | }, 404 | ], 405 | }, 406 | { 407 | label: 'BBB', 408 | value: 'ccc', 409 | children: [ 410 | { 411 | label: 'Child Node1', 412 | value: '0-1-0', 413 | key: '0-1-0', 414 | }, 415 | { 416 | label: 'Child Node2', 417 | value: '0-1-1', 418 | key: '0-1-1', 419 | }, 420 | { 421 | label: 'Child Node3', 422 | value: '0-1-2', 423 | key: '0-1-2', 424 | }, 425 | ], 426 | }, 427 | ]) 428 | } else if (linkage === 2) { 429 | resolve([ 430 | { 431 | label: 'CCC', 432 | value: 'ccc', 433 | children: [ 434 | { 435 | label: 'Child Node1', 436 | value: '0-0-0', 437 | key: '0-0-0', 438 | }, 439 | { 440 | label: 'Child Node2', 441 | value: '0-0-1', 442 | key: '0-0-1', 443 | }, 444 | { 445 | label: 'Child Node3', 446 | value: '0-0-2', 447 | key: '0-0-2', 448 | }, 449 | ], 450 | }, 451 | { 452 | label: 'DDD', 453 | value: 'ddd', 454 | children: [ 455 | { 456 | label: 'Child Node1', 457 | value: '0-1-0', 458 | key: '0-1-0', 459 | }, 460 | { 461 | label: 'Child Node2', 462 | value: '0-1-1', 463 | key: '0-1-1', 464 | }, 465 | { 466 | label: 'Child Node3', 467 | value: '0-1-2', 468 | key: '0-1-2', 469 | }, 470 | ], 471 | }, 472 | ]) 473 | } 474 | }, 1500) 475 | }) 476 | } 477 | 478 | const useAsyncDataSource = (service) => (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 | const form = createForm() 489 | 490 | const schema = { 491 | type: 'object', 492 | properties: { 493 | linkage: { 494 | type: 'string', 495 | label: '联动选择框', 496 | enum: [ 497 | { label: '发请求1', value: 1 }, 498 | { label: '发请求2', value: 2 }, 499 | ], 500 | 'x-decorator': 'FormItem', 501 | 'x-component': 'Select', 502 | 'x-component-props': { 503 | style: { 504 | width: 200, 505 | }, 506 | }, 507 | }, 508 | select: { 509 | type: 'string', 510 | label: '异步选择框', 511 | 'x-decorator': 'FormItem', 512 | 'x-component': 'TreeSelect', 513 | 'x-component-props': { 514 | style: { 515 | width: 200, 516 | }, 517 | }, 518 | 'x-reactions': ['{{useAsyncDataSource(loadData)}}'], 519 | }, 520 | }, 521 | } 522 | 523 | export default () => ( 524 | <FormProvider form={form}> 525 | <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} /> 526 | <FormButtonGroup> 527 | <Submit onSubmit={console.log}>提交</Submit> 528 | </FormButtonGroup> 529 | </FormProvider> 530 | ) 531 | ``` 532 | 533 | ## 纯 JSX 同步数据源案例 534 | 535 | ```tsx 536 | import React from 'react' 537 | import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/next' 538 | import { createForm } from '@formily/core' 539 | import { FormProvider, Field } from '@formily/react' 540 | 541 | const form = createForm() 542 | 543 | export default () => ( 544 | <FormProvider form={form}> 545 | <Field 546 | name="select" 547 | label="选择框" 548 | dataSource={[ 549 | { 550 | label: '选项1', 551 | value: 1, 552 | children: [ 553 | { 554 | label: 'Child Node1', 555 | value: '0-0-0', 556 | key: '0-0-0', 557 | }, 558 | { 559 | label: 'Child Node2', 560 | value: '0-0-1', 561 | key: '0-0-1', 562 | }, 563 | { 564 | label: 'Child Node3', 565 | value: '0-0-2', 566 | key: '0-0-2', 567 | }, 568 | ], 569 | }, 570 | { 571 | label: '选项2', 572 | value: 2, 573 | children: [ 574 | { 575 | label: 'Child Node3', 576 | value: '0-1-0', 577 | key: '0-1-0', 578 | }, 579 | { 580 | label: 'Child Node4', 581 | value: '0-1-1', 582 | key: '0-1-1', 583 | }, 584 | { 585 | label: 'Child Node5', 586 | value: '0-1-2', 587 | key: '0-1-2', 588 | }, 589 | ], 590 | }, 591 | ]} 592 | decorator={[FormItem]} 593 | component={[ 594 | TreeSelect, 595 | { 596 | style: { 597 | width: 200, 598 | }, 599 | }, 600 | ]} 601 | /> 602 | <FormButtonGroup> 603 | <Submit onSubmit={console.log}>提交</Submit> 604 | </FormButtonGroup> 605 | </FormProvider> 606 | ) 607 | ``` 608 | 609 | ## 纯 JSX 异步联动数据源案例 610 | 611 | ```tsx 612 | import React from 'react' 613 | import { 614 | TreeSelect, 615 | Select, 616 | FormItem, 617 | FormButtonGroup, 618 | Submit, 619 | } from '@formily/next' 620 | import { 621 | createForm, 622 | onFieldReact, 623 | FormPathPattern, 624 | FieldType, 625 | } from '@formily/core' 626 | import { FormProvider, Field } from '@formily/react' 627 | import { action } from '@formily/reactive' 628 | 629 | const useAsyncDataSource = ( 630 | pattern: FormPathPattern, 631 | service: (field: FieldType) => Promise<{ label: string; value: any }[]> 632 | ) => { 633 | onFieldReact(pattern, (field) => { 634 | field.loading = true 635 | service(field).then( 636 | action.bound((data) => { 637 | field.dataSource = data 638 | field.loading = false 639 | }) 640 | ) 641 | }) 642 | } 643 | 644 | const form = createForm({ 645 | effects: () => { 646 | useAsyncDataSource('select', async (field) => { 647 | const linkage = field.query('linkage').get('value') 648 | if (!linkage) return [] 649 | return new Promise((resolve) => { 650 | setTimeout(() => { 651 | if (linkage === 1) { 652 | resolve([ 653 | { 654 | label: 'AAA', 655 | value: 'aaa', 656 | children: [ 657 | { 658 | label: 'Child Node1', 659 | value: '0-0-0', 660 | key: '0-0-0', 661 | }, 662 | { 663 | label: 'Child Node2', 664 | value: '0-0-1', 665 | key: '0-0-1', 666 | }, 667 | { 668 | label: 'Child Node3', 669 | value: '0-0-2', 670 | key: '0-0-2', 671 | }, 672 | ], 673 | }, 674 | { 675 | label: 'BBB', 676 | value: 'ccc', 677 | children: [ 678 | { 679 | label: 'Child Node1', 680 | value: '0-1-0', 681 | key: '0-1-0', 682 | }, 683 | { 684 | label: 'Child Node2', 685 | value: '0-1-1', 686 | key: '0-1-1', 687 | }, 688 | { 689 | label: 'Child Node3', 690 | value: '0-1-2', 691 | key: '0-1-2', 692 | }, 693 | ], 694 | }, 695 | ]) 696 | } else if (linkage === 2) { 697 | resolve([ 698 | { 699 | label: 'CCC', 700 | value: 'ccc', 701 | children: [ 702 | { 703 | label: 'Child Node1', 704 | value: '0-0-0', 705 | key: '0-0-0', 706 | }, 707 | { 708 | label: 'Child Node2', 709 | value: '0-0-1', 710 | key: '0-0-1', 711 | }, 712 | { 713 | label: 'Child Node3', 714 | value: '0-0-2', 715 | key: '0-0-2', 716 | }, 717 | ], 718 | }, 719 | { 720 | label: 'DDD', 721 | value: 'ddd', 722 | children: [ 723 | { 724 | label: 'Child Node1', 725 | value: '0-1-0', 726 | key: '0-1-0', 727 | }, 728 | { 729 | label: 'Child Node2', 730 | value: '0-1-1', 731 | key: '0-1-1', 732 | }, 733 | { 734 | label: 'Child Node3', 735 | value: '0-1-2', 736 | key: '0-1-2', 737 | }, 738 | ], 739 | }, 740 | ]) 741 | } 742 | }, 1500) 743 | }) 744 | }) 745 | }, 746 | }) 747 | 748 | export default () => ( 749 | <FormProvider form={form}> 750 | <Field 751 | name="linkage" 752 | label="联动选择框" 753 | dataSource={[ 754 | { label: '发请求1', value: 1 }, 755 | { label: '发请求2', value: 2 }, 756 | ]} 757 | decorator={[FormItem]} 758 | component={[ 759 | Select, 760 | { 761 | style: { 762 | width: 200, 763 | }, 764 | }, 765 | ]} 766 | /> 767 | <Field 768 | name="select" 769 | label="异步选择框" 770 | decorator={[FormItem]} 771 | component={[ 772 | TreeSelect, 773 | { 774 | style: { 775 | width: 200, 776 | }, 777 | }, 778 | ]} 779 | /> 780 | <FormButtonGroup> 781 | <Submit onSubmit={console.log}>提交</Submit> 782 | </FormButtonGroup> 783 | </FormProvider> 784 | ) 785 | ``` 786 | 787 | ## API 788 | 789 | 参考 https://fusion.design/pc/component/basic/tree-select 790 | ``` -------------------------------------------------------------------------------- /packages/vue/src/__tests__/schema.markup.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createForm } from '@formily/core' 2 | import { useFieldSchema, useField, Schema } from '../' 3 | import { 4 | FormProvider, 5 | RecursionField, 6 | createSchemaField, 7 | } from '../vue2-components' 8 | import { render } from '@testing-library/vue' 9 | import { mount, createLocalVue } from '@vue/test-utils' 10 | import Vue, { CreateElement } from 'vue' 11 | import { defineComponent, h } from '@vue/composition-api' 12 | 13 | Vue.component('FormProvider', FormProvider) 14 | Vue.component('RecursionField', RecursionField) 15 | 16 | const Input = defineComponent({ 17 | props: ['value'], 18 | setup(props, { attrs, listeners }) { 19 | return () => { 20 | return h('input', { 21 | attrs: { 22 | ...attrs, 23 | value: props.value, 24 | 'data-testid': 'input', 25 | }, 26 | on: { 27 | ...listeners, 28 | input: listeners.change, 29 | }, 30 | }) 31 | } 32 | }, 33 | }) 34 | 35 | describe('markup schema field', () => { 36 | test('string', () => { 37 | const form = createForm() 38 | const { SchemaField, SchemaStringField } = createSchemaField({ 39 | components: { 40 | Input, 41 | }, 42 | }) 43 | const { queryByTestId } = render({ 44 | components: { SchemaField, SchemaStringField }, 45 | data() { 46 | return { 47 | form, 48 | } 49 | }, 50 | template: `<FormProvider :form="form"> 51 | <SchemaField> 52 | <SchemaStringField x-component="Input" /> 53 | </SchemaField> 54 | </FormProvider>`, 55 | }) 56 | expect(queryByTestId('input')).toBeVisible() 57 | }) 58 | 59 | test('boolean', () => { 60 | const form = createForm() 61 | const { SchemaField, SchemaBooleanField } = createSchemaField({ 62 | components: { 63 | Input, 64 | }, 65 | }) 66 | const { queryByTestId } = render({ 67 | components: { SchemaField, SchemaBooleanField }, 68 | data() { 69 | return { 70 | form, 71 | } 72 | }, 73 | template: `<FormProvider :form="form"> 74 | <SchemaField> 75 | <SchemaBooleanField x-component="Input" /> 76 | </SchemaField> 77 | </FormProvider>`, 78 | }) 79 | expect(queryByTestId('input')).toBeVisible() 80 | }) 81 | 82 | test('number', () => { 83 | const form = createForm() 84 | const { SchemaField, SchemaNumberField } = createSchemaField({ 85 | components: { 86 | Input, 87 | }, 88 | }) 89 | const { queryByTestId } = render({ 90 | components: { SchemaField, SchemaNumberField }, 91 | data() { 92 | return { 93 | form, 94 | } 95 | }, 96 | template: `<FormProvider :form="form"> 97 | <SchemaField> 98 | <SchemaNumberField x-component="Input" /> 99 | </SchemaField> 100 | </FormProvider>`, 101 | }) 102 | expect(queryByTestId('input')).toBeVisible() 103 | }) 104 | 105 | test('date', () => { 106 | const form = createForm() 107 | const { SchemaField, SchemaDateField } = createSchemaField({ 108 | components: { 109 | Input, 110 | }, 111 | }) 112 | const { queryByTestId } = render({ 113 | components: { SchemaField, SchemaDateField }, 114 | data() { 115 | return { 116 | form, 117 | } 118 | }, 119 | template: `<FormProvider :form="form"> 120 | <SchemaField> 121 | <SchemaDateField x-component="Input" /> 122 | </SchemaField> 123 | </FormProvider>`, 124 | }) 125 | expect(queryByTestId('input')).toBeVisible() 126 | }) 127 | 128 | test('datetime', () => { 129 | const form = createForm() 130 | const { SchemaField, SchemaDateTimeField } = createSchemaField({ 131 | components: { 132 | Input, 133 | }, 134 | }) 135 | const { queryByTestId } = render({ 136 | components: { SchemaField, SchemaDateTimeField }, 137 | data() { 138 | return { 139 | form, 140 | } 141 | }, 142 | template: `<FormProvider :form="form"> 143 | <SchemaField> 144 | <SchemaDateTimeField x-component="Input" /> 145 | </SchemaField> 146 | </FormProvider>`, 147 | }) 148 | expect(queryByTestId('input')).toBeVisible() 149 | }) 150 | 151 | test('void', () => { 152 | const form = createForm() 153 | const VoidComponent = { 154 | render(h: CreateElement) { 155 | return h( 156 | 'div', 157 | { attrs: { 'data-testid': 'void-component' } }, 158 | this.$slots.default 159 | ) 160 | }, 161 | } 162 | const { SchemaField, SchemaVoidField } = createSchemaField({ 163 | components: { 164 | VoidComponent, 165 | }, 166 | }) 167 | const { queryByTestId } = render({ 168 | components: { SchemaField, SchemaVoidField }, 169 | data() { 170 | return { 171 | form, 172 | } 173 | }, 174 | template: `<FormProvider :form="form"> 175 | <SchemaField> 176 | <SchemaVoidField x-component="VoidComponent" /> 177 | </SchemaField> 178 | </FormProvider>`, 179 | }) 180 | expect(queryByTestId('void-component')).toBeVisible() 181 | }) 182 | 183 | test('array', () => { 184 | const form = createForm() 185 | const components = createSchemaField({ 186 | components: { 187 | Input, 188 | }, 189 | }) 190 | render({ 191 | components: { ...components }, 192 | data() { 193 | return { 194 | form, 195 | } 196 | }, 197 | template: `<FormProvider :form="form"> 198 | <SchemaField> 199 | <SchemaArrayField> 200 | <SchemaObjectField> 201 | <SchemaStringField x-component="Input" /> 202 | </SchemaObjectField> 203 | <SchemaVoidField /> 204 | </SchemaArrayField> 205 | </SchemaField> 206 | </FormProvider>`, 207 | }) 208 | }) 209 | 210 | test('other', () => { 211 | const form = createForm() 212 | const components = createSchemaField({ 213 | components: { 214 | Input, 215 | }, 216 | }) 217 | render({ 218 | components: { ...components }, 219 | data() { 220 | return { 221 | form, 222 | } 223 | }, 224 | template: `<FormProvider :form="form"> 225 | <SchemaField> 226 | <SchemaMarkupField type="other"> 227 | <SchemaMarkupField /> 228 | </SchemaMarkupField> 229 | </SchemaField> 230 | </FormProvider>`, 231 | }) 232 | }) 233 | 234 | test('no parent', () => { 235 | const form = createForm() 236 | const components = createSchemaField({ 237 | components: { 238 | Input, 239 | }, 240 | }) 241 | render({ 242 | components: { ...components }, 243 | data() { 244 | return { 245 | form, 246 | } 247 | }, 248 | template: `<FormProvider :form="form"> 249 | <SchemaMarkupField type="other"> 250 | <SchemaMarkupField /> 251 | </SchemaMarkupField> 252 | </FormProvider>`, 253 | }) 254 | }) 255 | }) 256 | 257 | describe('recursion field', () => { 258 | test('onlyRenderProperties', () => { 259 | const form = createForm() 260 | 261 | const CustomObject = defineComponent({ 262 | setup() { 263 | const schemaRef = useFieldSchema() 264 | return () => { 265 | return h('div', { attrs: { 'data-testid': 'object' } }, [ 266 | h('RecursionField', { props: { schema: schemaRef.value } }), 267 | ]) 268 | } 269 | }, 270 | }) 271 | 272 | const CustomObject2 = defineComponent({ 273 | setup() { 274 | const fieldRef = useField() 275 | const schemaRef = useFieldSchema() 276 | return () => { 277 | const schema = schemaRef.value 278 | const field = fieldRef.value 279 | return h('div', { attrs: { 'data-testid': 'only-properties' } }, [ 280 | h('RecursionField', { 281 | props: { 282 | name: schema.name, 283 | basePath: field.address, 284 | schema, 285 | onlyRenderProperties: true, 286 | }, 287 | }), 288 | ]) 289 | } 290 | }, 291 | }) 292 | 293 | const components = createSchemaField({ 294 | components: { 295 | Input, 296 | CustomObject, 297 | CustomObject2, 298 | }, 299 | }) 300 | 301 | const { queryAllByTestId } = render({ 302 | components: components, 303 | data() { 304 | return { 305 | form, 306 | } 307 | }, 308 | template: `<FormProvider :form="form"> 309 | <SchemaField> 310 | <SchemaObjectField x-component="CustomObject"> 311 | <SchemaStringField x-component="Input" /> 312 | </SchemaObjectField> 313 | <SchemaObjectField x-component="CustomObject2"> 314 | <SchemaStringField x-component="Input" /> 315 | </SchemaObjectField> 316 | <SchemaVoidField x-component="CustomObject2"> 317 | <SchemaStringField x-component="Input" /> 318 | </SchemaVoidField> 319 | </SchemaField> 320 | </FormProvider>`, 321 | }) 322 | expect(queryAllByTestId('input').length).toEqual(3) 323 | expect(queryAllByTestId('object').length).toEqual(1) 324 | expect(queryAllByTestId('only-properties').length).toEqual(2) 325 | }) 326 | 327 | test('mapProperties', () => { 328 | const form = createForm() 329 | 330 | const CustomObject = defineComponent({ 331 | setup() { 332 | const schemaRef = useFieldSchema() 333 | return () => { 334 | return h('div', { attrs: { 'data-testid': 'object' } }, [ 335 | h('RecursionField', { 336 | props: { 337 | schema: schemaRef.value, 338 | mapProperties: (schema) => { 339 | schema.default = '123' 340 | return schema 341 | }, 342 | }, 343 | }), 344 | ]) 345 | } 346 | }, 347 | }) 348 | 349 | const CustomObject2 = defineComponent({ 350 | setup() { 351 | const schemaRef = useFieldSchema() 352 | return () => { 353 | const schema = schemaRef.value 354 | return h('div', { attrs: { 'data-testid': 'object' } }, [ 355 | h('RecursionField', { 356 | props: { 357 | schema, 358 | mapProperties: () => { 359 | return null 360 | }, 361 | }, 362 | }), 363 | ]) 364 | } 365 | }, 366 | }) 367 | 368 | const components = createSchemaField({ 369 | components: { 370 | Input, 371 | CustomObject, 372 | CustomObject2, 373 | }, 374 | }) 375 | 376 | const { queryAllByTestId } = render({ 377 | components: components, 378 | data() { 379 | return { 380 | form, 381 | } 382 | }, 383 | template: `<FormProvider :form="form"> 384 | <SchemaField> 385 | <SchemaObjectField x-component="CustomObject"> 386 | <SchemaStringField x-component="Input" /> 387 | </SchemaObjectField> 388 | <SchemaObjectField x-component="CustomObject2"> 389 | <SchemaStringField x-component="Input" /> 390 | </SchemaObjectField> 391 | </SchemaField> 392 | </FormProvider>`, 393 | }) 394 | expect(queryAllByTestId('input').length).toEqual(2) 395 | expect(queryAllByTestId('input')[0].getAttribute('value')).toEqual('123') 396 | expect(queryAllByTestId('input')[1].getAttribute('value')).toBeFalsy() 397 | }) 398 | 399 | test('filterProperties', () => { 400 | const form = createForm() 401 | 402 | const CustomObject = defineComponent({ 403 | setup() { 404 | const schemaRef = useFieldSchema() 405 | return () => { 406 | return h('div', { attrs: { 'data-testid': 'object' } }, [ 407 | h('RecursionField', { 408 | props: { 409 | schema: schemaRef.value, 410 | filterProperties: (schema: Schema) => { 411 | if (schema['x-component'] === 'Input') return false 412 | return true 413 | }, 414 | }, 415 | }), 416 | ]) 417 | } 418 | }, 419 | }) 420 | 421 | const CustomObject2 = defineComponent({ 422 | setup() { 423 | const schemaRef = useFieldSchema() 424 | return () => { 425 | return h('div', { attrs: { 'data-testid': 'object' } }, [ 426 | h('RecursionField', { 427 | props: { 428 | schema: schemaRef.value, 429 | filterProperties: (schema: Schema) => { 430 | if (schema['x-component'] === 'Input') return 431 | return true 432 | }, 433 | }, 434 | }), 435 | ]) 436 | } 437 | }, 438 | }) 439 | 440 | const components = createSchemaField({ 441 | components: { 442 | Input, 443 | CustomObject, 444 | CustomObject2, 445 | }, 446 | }) 447 | 448 | const { queryAllByTestId } = render({ 449 | components: components, 450 | data() { 451 | return { 452 | form, 453 | } 454 | }, 455 | template: `<FormProvider :form="form"> 456 | <SchemaField> 457 | <SchemaObjectField x-component="CustomObject"> 458 | <SchemaStringField x-component="Input" /> 459 | </SchemaObjectField> 460 | <SchemaObjectField x-component="CustomObject2"> 461 | <SchemaStringField x-component="Input" /> 462 | </SchemaObjectField> 463 | </SchemaField> 464 | </FormProvider>`, 465 | }) 466 | expect(queryAllByTestId('input').length).toEqual(1) 467 | expect(queryAllByTestId('object').length).toEqual(2) 468 | }) 469 | 470 | test('onlyRenderSelf', () => { 471 | const form = createForm() 472 | 473 | const CustomObject = defineComponent({ 474 | setup() { 475 | const schemaRef = useFieldSchema() 476 | return () => { 477 | return h('div', { attrs: { 'data-testid': 'object' } }, [ 478 | h('RecursionField', { 479 | props: { 480 | schema: schemaRef.value, 481 | onlyRenderSelf: true, 482 | }, 483 | }), 484 | ]) 485 | } 486 | }, 487 | }) 488 | 489 | const components = createSchemaField({ 490 | components: { 491 | Input, 492 | CustomObject, 493 | }, 494 | }) 495 | 496 | const { queryAllByTestId } = render({ 497 | components: components, 498 | data() { 499 | return { 500 | form, 501 | } 502 | }, 503 | template: `<FormProvider :form="form"> 504 | <SchemaField> 505 | <SchemaObjectField x-component="CustomObject"> 506 | <SchemaStringField x-component="Input" /> 507 | </SchemaObjectField> 508 | </SchemaField> 509 | </FormProvider>`, 510 | }) 511 | expect(queryAllByTestId('input').length).toEqual(0) 512 | expect(queryAllByTestId('object').length).toEqual(1) 513 | }) 514 | 515 | test('illegal schema', () => { 516 | const form = createForm() 517 | 518 | const CustomObject = defineComponent({ 519 | setup() { 520 | return () => { 521 | return h('div', { attrs: { 'data-testid': 'object' } }, [ 522 | h('RecursionField', { 523 | props: { 524 | schema: null, 525 | }, 526 | }), 527 | ]) 528 | } 529 | }, 530 | }) 531 | 532 | const CustomObject2 = defineComponent({ 533 | setup() { 534 | return () => { 535 | return h('div', { attrs: { 'data-testid': 'object' } }, [ 536 | h('RecursionField', { 537 | props: { 538 | schema: {}, 539 | }, 540 | }), 541 | ]) 542 | } 543 | }, 544 | }) 545 | 546 | const components = createSchemaField({ 547 | components: { 548 | Input, 549 | CustomObject, 550 | CustomObject2, 551 | }, 552 | }) 553 | 554 | const { queryByTestId } = render({ 555 | components: components, 556 | data() { 557 | return { 558 | form, 559 | } 560 | }, 561 | template: `<FormProvider :form="form"> 562 | <SchemaField> 563 | <SchemaObjectField x-component="CustomObject"> 564 | <SchemaStringField x-component="Input" /> 565 | </SchemaObjectField> 566 | <SchemaObjectField x-component="CustomObject2"> 567 | <SchemaStringField x-component="Input" /> 568 | </SchemaObjectField> 569 | </SchemaField> 570 | </FormProvider>`, 571 | }) 572 | expect(queryByTestId('input')).toBeNull() 573 | }) 574 | 575 | test('schema reactions', async () => { 576 | const div = document.createElement('div') 577 | document.body.appendChild(div) 578 | const form = createForm() 579 | const components = createSchemaField({ 580 | components: { 581 | Input, 582 | }, 583 | }) 584 | const localVue = createLocalVue() 585 | localVue.component('FormProvider', FormProvider) 586 | const TestComponent = { 587 | components: components, 588 | data() { 589 | return { 590 | form, 591 | reactions: [ 592 | { 593 | when: '{{$form.values.aaa === "123"}}', 594 | fulfill: { 595 | state: { 596 | visible: true, 597 | }, 598 | }, 599 | otherwise: { 600 | state: { 601 | visible: false, 602 | }, 603 | }, 604 | }, 605 | { 606 | when: '{{$self.value === "123"}}', 607 | target: 'ccc', 608 | fulfill: { 609 | schema: { 610 | 'x-visible': true, 611 | }, 612 | }, 613 | otherwise: { 614 | schema: { 615 | 'x-visible': false, 616 | }, 617 | }, 618 | }, 619 | ], 620 | } 621 | }, 622 | template: `<FormProvider :form="form"> 623 | <SchemaField> 624 | <SchemaStringField 625 | name="aaa" 626 | x-component="Input" 627 | :x-component-props="{ 628 | 'class': 'aaa', 629 | }" 630 | /> 631 | <SchemaStringField 632 | name="bbb" 633 | x-component="Input" 634 | :x-component-props="{ 635 | 'class': 'bbb', 636 | }" 637 | :x-reactions="reactions" 638 | /> 639 | <SchemaStringField 640 | name="ccc" 641 | x-component="Input" 642 | :x-component-props="{ 643 | 'class': 'ccc', 644 | }" 645 | /> 646 | </SchemaField> 647 | </FormProvider>`, 648 | } as any 649 | const wrapper = mount(TestComponent, { 650 | // attachTo: div, 651 | attachToDocument: true, 652 | localVue, 653 | }) 654 | expect(wrapper.find('.bbb').exists()).toBeFalsy() 655 | wrapper.find('.aaa').setValue('123') 656 | expect(form.query('aaa').get('value')).toEqual('123') 657 | await wrapper.vm.$forceUpdate() 658 | expect(wrapper.find('.bbb').exists()).toBeTruthy() 659 | expect(wrapper.find('.ccc').exists()).toBeFalsy() 660 | wrapper.find('.bbb').setValue('123') 661 | expect(form.query('bbb').get('value')).toEqual('123') 662 | await wrapper.vm.$forceUpdate() 663 | expect(wrapper.find('.ccc').exists()).toBeTruthy() 664 | wrapper.destroy() 665 | }) 666 | 667 | test('void field children', () => { 668 | const form = createForm() 669 | const VoidComponent = { 670 | render(h: CreateElement) { 671 | return h('div', this.$slots.default || 'placeholder') 672 | }, 673 | } 674 | const { SchemaField, SchemaVoidField } = createSchemaField({ 675 | components: { 676 | VoidComponent, 677 | }, 678 | }) 679 | const { queryByTestId } = render({ 680 | components: { SchemaField, SchemaVoidField }, 681 | data() { 682 | return { 683 | form, 684 | } 685 | }, 686 | template: `<FormProvider :form="form"> 687 | <SchemaField> 688 | <SchemaVoidField x-component="VoidComponent" :x-component-props="{ 'data-testid': 'void-component-1' }" /> 689 | <SchemaVoidField x-component="VoidComponent" :x-component-props="{ 'data-testid': 'void-component-2' }" x-content="content" /> 690 | </SchemaField> 691 | </FormProvider>`, 692 | }) 693 | expect(queryByTestId('void-component-1').textContent).toBe('placeholder') 694 | expect(queryByTestId('void-component-2').textContent).toBe('content') 695 | }) 696 | }) 697 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/SelectTable.zh-CN.md: -------------------------------------------------------------------------------- ```markdown 1 | # SelectTable 2 | 3 | > 表格选择组件 4 | 5 | ## Markup Schema 单选案例 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | FormItem, 16 | SelectTable, 17 | }, 18 | }) 19 | 20 | const form = createForm() 21 | 22 | export default () => { 23 | return ( 24 | <FormProvider form={form}> 25 | <SchemaField> 26 | <SchemaField.Object 27 | type="string" 28 | name="selectTable" 29 | x-decorator="FormItem" 30 | x-component="SelectTable" 31 | x-component-props={{ 32 | bordered: false, 33 | mode: 'single', 34 | }} 35 | enum={[ 36 | { key: '1', name: '标题1', description: '描述1' }, 37 | { key: '2', name: '标题2', description: '描述2' }, 38 | ]} 39 | > 40 | <SchemaField.Void 41 | name="name" 42 | title="标题" 43 | x-component="SelectTable.Column" 44 | /> 45 | <SchemaField.Void 46 | name="description" 47 | title="描述" 48 | x-component="SelectTable.Column" 49 | /> 50 | </SchemaField.Object> 51 | </SchemaField> 52 | <FormButtonGroup.FormItem> 53 | <Submit onSubmit={console.log}>提交</Submit> 54 | </FormButtonGroup.FormItem> 55 | </FormProvider> 56 | ) 57 | } 58 | ``` 59 | 60 | ## Markup Schema 筛选案例 61 | 62 | ```tsx 63 | import React from 'react' 64 | import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' 65 | import { createForm } from '@formily/core' 66 | import { FormProvider, createSchemaField } from '@formily/react' 67 | 68 | const SchemaField = createSchemaField({ 69 | components: { 70 | FormItem, 71 | SelectTable, 72 | }, 73 | }) 74 | 75 | const form = createForm() 76 | 77 | export default () => { 78 | return ( 79 | <FormProvider form={form}> 80 | <SchemaField> 81 | <SchemaField.Array 82 | type="array" 83 | name="selectTable" 84 | x-decorator="FormItem" 85 | x-component="SelectTable" 86 | x-component-props={{ 87 | bordered: false, 88 | showSearch: true, 89 | optionAsValue: true, 90 | }} 91 | enum={[ 92 | { key: '1', name: '标题1', description: '描述1' }, 93 | { key: '2', name: '标题2', description: '描述2' }, 94 | ]} 95 | > 96 | <SchemaField.Object> 97 | <SchemaField.Void 98 | name="name" 99 | title="标题" 100 | x-component="SelectTable.Column" 101 | /> 102 | <SchemaField.Void 103 | name="description" 104 | title="描述" 105 | x-component="SelectTable.Column" 106 | /> 107 | </SchemaField.Object> 108 | </SchemaField.Array> 109 | </SchemaField> 110 | <FormButtonGroup.FormItem> 111 | <Submit onSubmit={console.log}>提交</Submit> 112 | </FormButtonGroup.FormItem> 113 | </FormProvider> 114 | ) 115 | } 116 | ``` 117 | 118 | ## Markup Schema 异步数据源案例 119 | 120 | ```tsx 121 | import React from 'react' 122 | import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' 123 | import { createForm } from '@formily/core' 124 | import { FormProvider, createSchemaField } from '@formily/react' 125 | 126 | const SchemaField = createSchemaField({ 127 | components: { 128 | FormItem, 129 | SelectTable, 130 | }, 131 | }) 132 | 133 | const form = createForm() 134 | 135 | export default () => { 136 | const onSearch = (value) => { 137 | const field = form.query('selectTable').take() 138 | field.loading = true 139 | setTimeout(() => { 140 | field.setState({ 141 | dataSource: [ 142 | { 143 | key: '3', 144 | name: 'AAA' + value, 145 | description: 'aaa', 146 | }, 147 | { 148 | key: '4', 149 | name: 'BBB' + value, 150 | description: 'bbb', 151 | }, 152 | ], 153 | loading: false, 154 | }) 155 | }, 1500) 156 | } 157 | 158 | return ( 159 | <FormProvider form={form}> 160 | <SchemaField> 161 | <SchemaField.Object 162 | type="object" 163 | name="selectTable" 164 | x-decorator="FormItem" 165 | x-component="SelectTable" 166 | x-component-props={{ 167 | showSearch: true, 168 | filterOption: false, 169 | onSearch, 170 | }} 171 | enum={[ 172 | { key: '1', name: '标题1', description: '描述1' }, 173 | { key: '2', name: '标题2', description: '描述2' }, 174 | ]} 175 | > 176 | <SchemaField.Void 177 | name="name" 178 | title="标题" 179 | x-component="SelectTable.Column" 180 | /> 181 | <SchemaField.Void 182 | name="description" 183 | title="描述" 184 | x-component="SelectTable.Column" 185 | /> 186 | </SchemaField.Object> 187 | </SchemaField> 188 | <FormButtonGroup.FormItem> 189 | <Submit onSubmit={console.log}>提交</Submit> 190 | </FormButtonGroup.FormItem> 191 | </FormProvider> 192 | ) 193 | } 194 | ``` 195 | 196 | ## Markup Schema 阅读态案例 197 | 198 | ```tsx 199 | import React from 'react' 200 | import { 201 | Form, 202 | FormItem, 203 | FormButtonGroup, 204 | Submit, 205 | SelectTable, 206 | } from '@formily/antd' 207 | import { createForm } from '@formily/core' 208 | import { createSchemaField } from '@formily/react' 209 | 210 | const SchemaField = createSchemaField({ 211 | components: { 212 | FormItem, 213 | SelectTable, 214 | }, 215 | }) 216 | 217 | const form = createForm() 218 | 219 | export default () => { 220 | return ( 221 | <Form form={form} layout="vertical"> 222 | <SchemaField> 223 | <SchemaField.Object 224 | title="单选" 225 | type="string" 226 | name="selectTable1" 227 | x-decorator="FormItem" 228 | x-component="SelectTable" 229 | x-component-props={{ 230 | mode: 'single', 231 | }} 232 | default="1" 233 | enum={[ 234 | { key: '1', name: '标题1', description: '描述1' }, 235 | { key: '2', name: '标题2', description: '描述2' }, 236 | ]} 237 | x-read-pretty={true} 238 | > 239 | <SchemaField.Void 240 | name="name" 241 | title="标题" 242 | x-component="SelectTable.Column" 243 | /> 244 | <SchemaField.Void 245 | name="description" 246 | title="描述" 247 | x-component="SelectTable.Column" 248 | /> 249 | </SchemaField.Object> 250 | <SchemaField.Object 251 | title="单选 + optionAsValue" 252 | type="string" 253 | name="selectTable2" 254 | x-decorator="FormItem" 255 | x-component="SelectTable" 256 | x-component-props={{ 257 | mode: 'single', 258 | optionAsValue: true, 259 | }} 260 | default={{ key: '1', name: '标题1', description: '描述1' }} 261 | enum={[ 262 | { key: '1', name: '标题1', description: '描述1' }, 263 | { key: '2', name: '标题2', description: '描述2' }, 264 | ]} 265 | x-read-pretty={true} 266 | > 267 | <SchemaField.Void 268 | name="name" 269 | title="标题" 270 | x-component="SelectTable.Column" 271 | /> 272 | <SchemaField.Void 273 | name="description" 274 | title="描述" 275 | x-component="SelectTable.Column" 276 | /> 277 | </SchemaField.Object> 278 | <SchemaField.Array 279 | title="多选" 280 | type="array" 281 | name="selectTable3" 282 | x-decorator="FormItem" 283 | x-component="SelectTable" 284 | default={['1', '3']} 285 | enum={[ 286 | { key: '1', name: '标题1', description: '描述1' }, 287 | { key: '2', name: '标题2', description: '描述2' }, 288 | { key: '3', name: '标题3', description: '描述3' }, 289 | ]} 290 | x-read-pretty={true} 291 | > 292 | <SchemaField.Object> 293 | <SchemaField.Void 294 | name="name" 295 | title="标题" 296 | x-component="SelectTable.Column" 297 | /> 298 | <SchemaField.Void 299 | name="description" 300 | title="描述" 301 | x-component="SelectTable.Column" 302 | /> 303 | </SchemaField.Object> 304 | </SchemaField.Array> 305 | <SchemaField.Array 306 | title="多选 + optionAsValue" 307 | type="array" 308 | name="selectTable4" 309 | x-decorator="FormItem" 310 | x-component="SelectTable" 311 | x-component-props={{ 312 | optionAsValue: true, 313 | }} 314 | default={[ 315 | { key: '1', name: '标题1', description: '描述1' }, 316 | { key: '3', name: '标题3', description: '描述3' }, 317 | ]} 318 | enum={[ 319 | { key: '1', name: '标题1', description: '描述1' }, 320 | { key: '2', name: '标题2', description: '描述2' }, 321 | { key: '3', name: '标题3', description: '描述3' }, 322 | ]} 323 | x-read-pretty={true} 324 | > 325 | <SchemaField.Object> 326 | <SchemaField.Void 327 | name="name" 328 | title="标题" 329 | x-component="SelectTable.Column" 330 | /> 331 | <SchemaField.Void 332 | name="description" 333 | title="描述" 334 | x-component="SelectTable.Column" 335 | /> 336 | </SchemaField.Object> 337 | </SchemaField.Array> 338 | </SchemaField> 339 | <FormButtonGroup.FormItem> 340 | <Submit onSubmit={console.log}>提交</Submit> 341 | </FormButtonGroup.FormItem> 342 | </Form> 343 | ) 344 | } 345 | ``` 346 | 347 | ## JSON Schema 多选案例 348 | 349 | ```tsx 350 | import React from 'react' 351 | import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' 352 | import { createForm } from '@formily/core' 353 | import { FormProvider, createSchemaField } from '@formily/react' 354 | 355 | const SchemaField = createSchemaField({ 356 | components: { 357 | SelectTable, 358 | FormItem, 359 | }, 360 | }) 361 | 362 | const form = createForm() 363 | 364 | const schema = { 365 | type: 'object', 366 | properties: { 367 | selectTable: { 368 | type: 'array', 369 | 'x-decorator': 'FormItem', 370 | 'x-component': 'SelectTable', 371 | 'x-component-props': { 372 | bordered: false, 373 | mode: 'multiple', 374 | }, 375 | enum: [ 376 | { key: '1', name: '标题1', description: '描述1' }, 377 | { key: '2', name: '标题2', description: '描述2' }, 378 | ], 379 | properties: { 380 | name: { 381 | title: '标题', 382 | type: 'string', 383 | 'x-component': 'SelectTable.Column', 384 | 'x-component-props': { 385 | width: '40%', 386 | }, 387 | }, 388 | description: { 389 | title: '描述', 390 | type: 'string', 391 | 'x-component': 'SelectTable.Column', 392 | 'x-component-props': { 393 | width: '60%', 394 | }, 395 | }, 396 | }, 397 | }, 398 | }, 399 | } 400 | 401 | export default () => ( 402 | <FormProvider form={form}> 403 | <SchemaField schema={schema} /> 404 | <FormButtonGroup> 405 | <Submit onSubmit={console.log}>提交</Submit> 406 | </FormButtonGroup> 407 | </FormProvider> 408 | ) 409 | ``` 410 | 411 | ## JSON Schema 自定义筛选案例 412 | 413 | ```tsx 414 | import React from 'react' 415 | import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' 416 | import { createForm } from '@formily/core' 417 | import { FormProvider, createSchemaField } from '@formily/react' 418 | 419 | const SchemaField = createSchemaField({ 420 | components: { 421 | SelectTable, 422 | FormItem, 423 | }, 424 | }) 425 | 426 | const form = createForm() 427 | 428 | const schema = { 429 | type: 'object', 430 | properties: { 431 | selectTable: { 432 | type: 'array', 433 | 'x-decorator': 'FormItem', 434 | 'x-component': 'SelectTable', 435 | 'x-component-props': { 436 | bordered: false, 437 | showSearch: true, 438 | primaryKey: 'key', 439 | isTree: true, 440 | filterOption: (input, option) => 441 | option.description.toLowerCase().indexOf(input.toLowerCase()) >= 0, 442 | filterSort: (optionA, optionB) => 443 | optionA.description 444 | .toLowerCase() 445 | .localeCompare(optionB.description.toLowerCase()), 446 | optionAsValue: true, 447 | rowSelection: { 448 | checkStrictly: false, 449 | }, 450 | }, 451 | enum: [ 452 | { key: '1', name: '标题1', description: 'A-描述' }, 453 | { 454 | key: '2', 455 | name: '标题2', 456 | description: 'X-描述', 457 | children: [ 458 | { 459 | key: '2-1', 460 | name: '标题2-1', 461 | description: 'Y-描述', 462 | children: [ 463 | { key: '2-1-1', name: '标题2-1-1', description: 'Z-描述' }, 464 | ], 465 | }, 466 | { 467 | key: '2-2', 468 | name: '标题2-2', 469 | description: 'YY-描述', 470 | }, 471 | ], 472 | }, 473 | { key: '3', name: '标题3', description: 'C-描述' }, 474 | ], 475 | properties: { 476 | name: { 477 | title: '标题', 478 | type: 'string', 479 | 'x-component': 'SelectTable.Column', 480 | 'x-component-props': { 481 | width: '40%', 482 | }, 483 | }, 484 | description: { 485 | title: '描述', 486 | type: 'string', 487 | 'x-component': 'SelectTable.Column', 488 | 'x-component-props': { 489 | width: '60%', 490 | }, 491 | }, 492 | }, 493 | }, 494 | }, 495 | } 496 | 497 | export default () => ( 498 | <FormProvider form={form}> 499 | <SchemaField schema={schema} /> 500 | <FormButtonGroup> 501 | <Submit onSubmit={console.log}>提交</Submit> 502 | </FormButtonGroup> 503 | </FormProvider> 504 | ) 505 | ``` 506 | 507 | ## JSON Schema 异步数据源案例 508 | 509 | ```tsx 510 | import React from 'react' 511 | import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' 512 | import { createForm } from '@formily/core' 513 | import { FormProvider, createSchemaField } from '@formily/react' 514 | 515 | const SchemaField = createSchemaField({ 516 | components: { 517 | SelectTable, 518 | FormItem, 519 | }, 520 | }) 521 | 522 | const loadData = async (value) => { 523 | return new Promise((resolve) => { 524 | setTimeout(() => { 525 | resolve([ 526 | { key: '3', name: 'AAA' + value, description: 'aaa' }, 527 | { key: '4', name: 'BBB' + value, description: 'bbb' }, 528 | ]) 529 | }, 1500) 530 | }) 531 | } 532 | 533 | const useAsyncDataSource = (service, field) => (value) => { 534 | field.loading = true 535 | service(value).then((data) => { 536 | field.setState({ 537 | dataSource: data, 538 | loading: false, 539 | }) 540 | }) 541 | } 542 | 543 | const form = createForm() 544 | 545 | const schema = { 546 | type: 'object', 547 | properties: { 548 | selectTable: { 549 | type: 'array', 550 | 'x-decorator': 'FormItem', 551 | 'x-component': 'SelectTable', 552 | 'x-component-props': { 553 | showSearch: true, 554 | filterOption: false, 555 | onSearch: '{{useAsyncDataSource(loadData,$self)}}', 556 | }, 557 | enum: [ 558 | { key: '1', name: '标题1', description: '描述1' }, 559 | { key: '2', name: '标题2', description: '描述2' }, 560 | ], 561 | properties: { 562 | name: { 563 | title: '标题', 564 | type: 'string', 565 | 'x-component': 'SelectTable.Column', 566 | 'x-component-props': { 567 | width: '40%', 568 | }, 569 | }, 570 | description: { 571 | title: '描述', 572 | type: 'string', 573 | 'x-component': 'SelectTable.Column', 574 | 'x-component-props': { 575 | width: '60%', 576 | }, 577 | }, 578 | }, 579 | }, 580 | }, 581 | } 582 | 583 | export default () => ( 584 | <FormProvider form={form}> 585 | <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} /> 586 | <FormButtonGroup> 587 | <Submit onSubmit={console.log}>提交</Submit> 588 | </FormButtonGroup> 589 | </FormProvider> 590 | ) 591 | ``` 592 | 593 | ## 纯 JSX 案例 594 | 595 | ```tsx 596 | import React from 'react' 597 | import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' 598 | import { createForm } from '@formily/core' 599 | import { FormProvider, Field } from '@formily/react' 600 | 601 | const form = createForm() 602 | 603 | export default () => ( 604 | <FormProvider form={form}> 605 | <Field 606 | name="SelectTable" 607 | dataSource={[ 608 | { key: '1', name: '标题1', description: '描述1' }, 609 | { key: '2', name: '标题2', description: '描述2' }, 610 | ]} 611 | decorator={[FormItem]} 612 | component={[ 613 | SelectTable, 614 | { 615 | columns: [ 616 | { dataIndex: 'name', title: '标题' }, 617 | { dataIndex: 'description', title: '描述' }, 618 | ], 619 | }, 620 | ]} 621 | /> 622 | <FormButtonGroup> 623 | <Submit onSubmit={console.log}>提交</Submit> 624 | </FormButtonGroup> 625 | </FormProvider> 626 | ) 627 | ``` 628 | 629 | ## API 630 | 631 | ### SelectTable 632 | 633 | | 属性名 | 类型 | 描述 | 默认值 | 634 | | ------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------ | 635 | | mode | `'multiple' \| 'single'` | 设置 SelectTable 模式为单选或多选 | `'multiple'` | 636 | | valueType | `'all' \| 'parent' \| 'child' \| 'path'` | 返回值类型,checkStrictly 设置为 `false` 时有效 | `'all'` | 637 | | optionAsValue | boolean | 使用表格行数据作为值,valueType 值为 `'path'` 时无效 | false | 638 | | showSearch | boolean | 是否显示搜索组件 | false | 639 | | searchProps | object | Search 组件属性 | - | 640 | | primaryKey | `string \| (record) => string` | 表格行 key 的取值 | `'key'` | 641 | | filterOption | `boolean \| (inputValue, option) => boolean` | 是否根据输入项进行筛选。当其为一个函数时,会接收 inputValue option 两个参数,当 option 符合筛选条件时,应返回 true,反之则返回 false | true | 642 | | filterSort | (optionA, optionB) => number | 搜索时对筛选结果项的排序函数, 类似 Array.sort 里的 compareFunction | - | 643 | | onSearch | 文本框值变化时回调 | (inputValue) => void | - | 644 | 645 | 参考 https://ant.design/components/table-cn/ 646 | 647 | ### rowSelection 648 | 649 | | 属性名 | 类型 | 描述 | 默认值 | 650 | | ------------- | ------- | ------------------------------------------------------------ | ------ | 651 | | checkStrictly | boolean | checkable 状态下节点选择完全受控(父子数据选中状态不再关联) | true | 652 | 653 | 参考 https://ant.design/components/table/#rowSelection 654 | 655 | ### SelectTable.Column 656 | 657 | 参考 https://ant.design/components/table-cn/ Table.Column 属性 658 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/TreeSelect.md: -------------------------------------------------------------------------------- ```markdown 1 | # TreeSelect 2 | 3 | > Tree selector 4 | 5 | ## Markup Schema synchronization data source case 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { TreeSelect, 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 | TreeSelect, 16 | FormItem, 17 | }, 18 | }) 19 | 20 | const form = createForm() 21 | 22 | export default () => ( 23 | <FormProvider form={form}> 24 | <SchemaField> 25 | <SchemaField.Number 26 | name="select" 27 | title="select box" 28 | x-decorator="FormItem" 29 | x-component="TreeSelect" 30 | enum={[ 31 | { 32 | label: 'Option 1', 33 | value: 1, 34 | children: [ 35 | { 36 | title: 'Child Node1', 37 | value: '0-0-0', 38 | key: '0-0-0', 39 | }, 40 | { 41 | title: 'Child Node2', 42 | value: '0-0-1', 43 | key: '0-0-1', 44 | }, 45 | { 46 | title: 'Child Node3', 47 | value: '0-0-2', 48 | key: '0-0-2', 49 | }, 50 | ], 51 | }, 52 | { 53 | label: 'Option 2', 54 | value: 2, 55 | children: [ 56 | { 57 | title: 'Child Node3', 58 | value: '0-1-0', 59 | key: '0-1-0', 60 | }, 61 | { 62 | title: 'Child Node4', 63 | value: '0-1-1', 64 | key: '0-1-1', 65 | }, 66 | { 67 | title: 'Child Node5', 68 | value: '0-1-2', 69 | key: '0-1-2', 70 | }, 71 | ], 72 | }, 73 | ]} 74 | x-component-props={{ 75 | style: { 76 | width: 200, 77 | }, 78 | }} 79 | /> 80 | </SchemaField> 81 | <FormButtonGroup> 82 | <Submit onSubmit={console.log}>Submit</Submit> 83 | </FormButtonGroup> 84 | </FormProvider> 85 | ) 86 | ``` 87 | 88 | ## Markup Schema Asynchronous Linkage Data Source Case 89 | 90 | ```tsx 91 | import React from 'react' 92 | import { 93 | TreeSelect, 94 | Select, 95 | FormItem, 96 | FormButtonGroup, 97 | Submit, 98 | } from '@formily/antd' 99 | import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core' 100 | import { FormProvider, createSchemaField } from '@formily/react' 101 | import { action } from '@formily/reactive' 102 | 103 | const SchemaField = createSchemaField({ 104 | components: { 105 | Select, 106 | TreeSelect, 107 | FormItem, 108 | }, 109 | }) 110 | 111 | const useAsyncDataSource = ( 112 | pattern: FormPathPattern, 113 | service: (field: Field) => Promise<{ label: string; value: any }[]> 114 | ) => { 115 | onFieldReact(pattern, (field) => { 116 | field.loading = true 117 | service(field).then( 118 | action.bound((data) => { 119 | field.dataSource = data 120 | field.loading = false 121 | }) 122 | ) 123 | }) 124 | } 125 | 126 | const form = createForm({ 127 | effects: () => { 128 | useAsyncDataSource('select', async (field) => { 129 | const linkage = field.query('linkage').get('value') 130 | if (!linkage) return [] 131 | return new Promise((resolve) => { 132 | setTimeout(() => { 133 | if (linkage === 1) { 134 | resolve([ 135 | { 136 | label: 'AAA', 137 | value: 'aaa', 138 | children: [ 139 | { 140 | title: 'Child Node1', 141 | value: '0-0-0', 142 | key: '0-0-0', 143 | }, 144 | { 145 | title: 'Child Node2', 146 | value: '0-0-1', 147 | key: '0-0-1', 148 | }, 149 | { 150 | title: 'Child Node3', 151 | value: '0-0-2', 152 | key: '0-0-2', 153 | }, 154 | ], 155 | }, 156 | { 157 | label: 'BBB', 158 | value: 'ccc', 159 | children: [ 160 | { 161 | title: 'Child Node1', 162 | value: '0-1-0', 163 | key: '0-1-0', 164 | }, 165 | { 166 | title: 'Child Node2', 167 | value: '0-1-1', 168 | key: '0-1-1', 169 | }, 170 | { 171 | title: 'Child Node3', 172 | value: '0-1-2', 173 | key: '0-1-2', 174 | }, 175 | ], 176 | }, 177 | ]) 178 | } else if (linkage === 2) { 179 | resolve([ 180 | { 181 | label: 'CCC', 182 | value: 'ccc', 183 | children: [ 184 | { 185 | title: 'Child Node1', 186 | value: '0-0-0', 187 | key: '0-0-0', 188 | }, 189 | { 190 | title: 'Child Node2', 191 | value: '0-0-1', 192 | key: '0-0-1', 193 | }, 194 | { 195 | title: 'Child Node3', 196 | value: '0-0-2', 197 | key: '0-0-2', 198 | }, 199 | ], 200 | }, 201 | { 202 | label: 'DDD', 203 | value: 'ddd', 204 | children: [ 205 | { 206 | title: 'Child Node1', 207 | value: '0-1-0', 208 | key: '0-1-0', 209 | }, 210 | { 211 | title: 'Child Node2', 212 | value: '0-1-1', 213 | key: '0-1-1', 214 | }, 215 | { 216 | title: 'Child Node3', 217 | value: '0-1-2', 218 | key: '0-1-2', 219 | }, 220 | ], 221 | }, 222 | ]) 223 | } 224 | }, 1500) 225 | }) 226 | }) 227 | }, 228 | }) 229 | 230 | export default () => ( 231 | <FormProvider form={form}> 232 | <SchemaField> 233 | <SchemaField.Number 234 | name="linkage" 235 | title="Linkage selection box" 236 | x-decorator="FormItem" 237 | x-component="Select" 238 | enum={[ 239 | { label: 'Request 1', value: 1 }, 240 | { label: 'Request 2', value: 2 }, 241 | ]} 242 | x-component-props={{ 243 | style: { 244 | width: 200, 245 | }, 246 | }} 247 | /> 248 | <SchemaField.String 249 | name="select" 250 | title="Asynchronous select box" 251 | x-decorator="FormItem" 252 | x-component="TreeSelect" 253 | x-component-props={{ 254 | style: { 255 | width: 200, 256 | }, 257 | }} 258 | /> 259 | </SchemaField> 260 | <FormButtonGroup> 261 | <Submit onSubmit={console.log}>Submit</Submit> 262 | </FormButtonGroup> 263 | </FormProvider> 264 | ) 265 | ``` 266 | 267 | ## JSON Schema synchronization data source case 268 | 269 | ```tsx 270 | import React from 'react' 271 | import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/antd' 272 | import { createForm } from '@formily/core' 273 | import { FormProvider, createSchemaField } from '@formily/react' 274 | 275 | const SchemaField = createSchemaField({ 276 | components: { 277 | TreeSelect, 278 | FormItem, 279 | }, 280 | }) 281 | 282 | const form = createForm() 283 | 284 | const schema = { 285 | type: 'object', 286 | properties: { 287 | select: { 288 | type: 'string', 289 | title: 'Select box', 290 | 'x-decorator': 'FormItem', 291 | 'x-component': 'TreeSelect', 292 | enum: [ 293 | { 294 | label: 'Option 1', 295 | value: 1, 296 | children: [ 297 | { 298 | title: 'Child Node1', 299 | value: '0-0-0', 300 | key: '0-0-0', 301 | }, 302 | { 303 | title: 'Child Node2', 304 | value: '0-0-1', 305 | key: '0-0-1', 306 | }, 307 | { 308 | title: 'Child Node3', 309 | value: '0-0-2', 310 | key: '0-0-2', 311 | }, 312 | ], 313 | }, 314 | { 315 | label: 'Option 2', 316 | value: 2, 317 | children: [ 318 | { 319 | title: 'Child Node1', 320 | value: '0-1-0', 321 | key: '0-1-0', 322 | }, 323 | { 324 | title: 'Child Node2', 325 | value: '0-1-1', 326 | key: '0-1-1', 327 | }, 328 | { 329 | title: 'Child Node3', 330 | value: '0-1-2', 331 | key: '0-1-2', 332 | }, 333 | ], 334 | }, 335 | ], 336 | 'x-component-props': { 337 | style: { 338 | width: 200, 339 | }, 340 | }, 341 | }, 342 | }, 343 | } 344 | 345 | export default () => ( 346 | <FormProvider form={form}> 347 | <SchemaField schema={schema} /> 348 | <FormButtonGroup> 349 | <Submit onSubmit={console.log}>Submit</Submit> 350 | </FormButtonGroup> 351 | </FormProvider> 352 | ) 353 | ``` 354 | 355 | ## JSON Schema asynchronous linkage data source case 356 | 357 | ```tsx 358 | import React from 'react' 359 | import { 360 | TreeSelect, 361 | Select, 362 | FormItem, 363 | FormButtonGroup, 364 | Submit, 365 | } from '@formily/antd' 366 | import { createForm } from '@formily/core' 367 | import { FormProvider, createSchemaField } from '@formily/react' 368 | import { action } from '@formily/reactive' 369 | 370 | const SchemaField = createSchemaField({ 371 | components: { 372 | Select, 373 | TreeSelect, 374 | FormItem, 375 | }, 376 | }) 377 | 378 | const loadData = async (field) => { 379 | const linkage = field.query('linkage').get('value') 380 | if (!linkage) return [] 381 | return new Promise((resolve) => { 382 | setTimeout(() => { 383 | if (linkage === 1) { 384 | resolve([ 385 | { 386 | label: 'AAA', 387 | value: 'aaa', 388 | children: [ 389 | { 390 | title: 'Child Node1', 391 | value: '0-0-0', 392 | key: '0-0-0', 393 | }, 394 | { 395 | title: 'Child Node2', 396 | value: '0-0-1', 397 | key: '0-0-1', 398 | }, 399 | { 400 | title: 'Child Node3', 401 | value: '0-0-2', 402 | key: '0-0-2', 403 | }, 404 | ], 405 | }, 406 | { 407 | label: 'BBB', 408 | value: 'ccc', 409 | children: [ 410 | { 411 | title: 'Child Node1', 412 | value: '0-1-0', 413 | key: '0-1-0', 414 | }, 415 | { 416 | title: 'Child Node2', 417 | value: '0-1-1', 418 | key: '0-1-1', 419 | }, 420 | { 421 | title: 'Child Node3', 422 | value: '0-1-2', 423 | key: '0-1-2', 424 | }, 425 | ], 426 | }, 427 | ]) 428 | } else if (linkage === 2) { 429 | resolve([ 430 | { 431 | label: 'CCC', 432 | value: 'ccc', 433 | children: [ 434 | { 435 | title: 'Child Node1', 436 | value: '0-0-0', 437 | key: '0-0-0', 438 | }, 439 | { 440 | title: 'Child Node2', 441 | value: '0-0-1', 442 | key: '0-0-1', 443 | }, 444 | { 445 | title: 'Child Node3', 446 | value: '0-0-2', 447 | key: '0-0-2', 448 | }, 449 | ], 450 | }, 451 | { 452 | label: 'DDD', 453 | value: 'ddd', 454 | children: [ 455 | { 456 | title: 'Child Node1', 457 | value: '0-1-0', 458 | key: '0-1-0', 459 | }, 460 | { 461 | title: 'Child Node2', 462 | value: '0-1-1', 463 | key: '0-1-1', 464 | }, 465 | { 466 | title: 'Child Node3', 467 | value: '0-1-2', 468 | key: '0-1-2', 469 | }, 470 | ], 471 | }, 472 | ]) 473 | } 474 | }, 1500) 475 | }) 476 | } 477 | 478 | const useAsyncDataSource = (service) => (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 | const form = createForm() 489 | 490 | const schema = { 491 | type: 'object', 492 | properties: { 493 | linkage: { 494 | type: 'string', 495 | title: 'Linkage selection box', 496 | enum: [ 497 | { label: 'Request 1', value: 1 }, 498 | { label: 'Request 2', value: 2 }, 499 | ], 500 | 'x-decorator': 'FormItem', 501 | 'x-component': 'Select', 502 | 'x-component-props': { 503 | style: { 504 | width: 200, 505 | }, 506 | }, 507 | }, 508 | select: { 509 | type: 'string', 510 | title: 'Asynchronous selection box', 511 | 'x-decorator': 'FormItem', 512 | 'x-component': 'TreeSelect', 513 | 'x-component-props': { 514 | style: { 515 | width: 200, 516 | }, 517 | }, 518 | 'x-reactions': ['{{useAsyncDataSource(loadData)}}'], 519 | }, 520 | }, 521 | } 522 | 523 | export default () => ( 524 | <FormProvider form={form}> 525 | <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} /> 526 | <FormButtonGroup> 527 | <Submit onSubmit={console.log}>Submit</Submit> 528 | </FormButtonGroup> 529 | </FormProvider> 530 | ) 531 | ``` 532 | 533 | ## Pure JSX synchronization data source case 534 | 535 | ```tsx 536 | import React from 'react' 537 | import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/antd' 538 | import { createForm } from '@formily/core' 539 | import { FormProvider, Field } from '@formily/react' 540 | 541 | const form = createForm() 542 | 543 | export default () => ( 544 | <FormProvider form={form}> 545 | <Field 546 | name="select" 547 | title="select box" 548 | dataSource={[ 549 | { 550 | label: 'Option 1', 551 | value: 1, 552 | children: [ 553 | { 554 | title: 'Child Node1', 555 | value: '0-0-0', 556 | key: '0-0-0', 557 | }, 558 | { 559 | title: 'Child Node2', 560 | value: '0-0-1', 561 | key: '0-0-1', 562 | }, 563 | { 564 | title: 'Child Node3', 565 | value: '0-0-2', 566 | key: '0-0-2', 567 | }, 568 | ], 569 | }, 570 | { 571 | label: 'Option 2', 572 | value: 2, 573 | children: [ 574 | { 575 | title: 'Child Node3', 576 | value: '0-1-0', 577 | key: '0-1-0', 578 | }, 579 | { 580 | title: 'Child Node4', 581 | value: '0-1-1', 582 | key: '0-1-1', 583 | }, 584 | { 585 | title: 'Child Node5', 586 | value: '0-1-2', 587 | key: '0-1-2', 588 | }, 589 | ], 590 | }, 591 | ]} 592 | decorator={[FormItem]} 593 | component={[TreeSelect]} 594 | /> 595 | <FormButtonGroup> 596 | <Submit onSubmit={console.log}>Submit</Submit> 597 | </FormButtonGroup> 598 | </FormProvider> 599 | ) 600 | ``` 601 | 602 | ## Pure JSX asynchronous linkage data source case 603 | 604 | ```tsx 605 | import React from 'react' 606 | import { 607 | TreeSelect, 608 | Select, 609 | FormItem, 610 | FormButtonGroup, 611 | Submit, 612 | } from '@formily/antd' 613 | import { 614 | createForm, 615 | onFieldReact, 616 | FormPathPattern, 617 | Field as FieldType, 618 | } from '@formily/core' 619 | import { FormProvider, Field } from '@formily/react' 620 | import { action } from '@formily/reactive' 621 | 622 | const useAsyncDataSource = ( 623 | pattern: FormPathPattern, 624 | service: (field: FieldType) => Promise<{ label: string; value: any }[]> 625 | ) => { 626 | onFieldReact(pattern, (field) => { 627 | field.loading = true 628 | service(field).then( 629 | action.bound((data) => { 630 | field.dataSource = data 631 | field.loading = false 632 | }) 633 | ) 634 | }) 635 | } 636 | 637 | const form = createForm({ 638 | effects: () => { 639 | useAsyncDataSource('select', async (field) => { 640 | const linkage = field.query('linkage').get('value') 641 | if (!linkage) return [] 642 | return new Promise((resolve) => { 643 | setTimeout(() => { 644 | if (linkage === 1) { 645 | resolve([ 646 | { 647 | label: 'AAA', 648 | value: 'aaa', 649 | children: [ 650 | { 651 | title: 'Child Node1', 652 | value: '0-0-0', 653 | key: '0-0-0', 654 | }, 655 | { 656 | title: 'Child Node2', 657 | value: '0-0-1', 658 | key: '0-0-1', 659 | }, 660 | { 661 | title: 'Child Node3', 662 | value: '0-0-2', 663 | key: '0-0-2', 664 | }, 665 | ], 666 | }, 667 | { 668 | label: 'BBB', 669 | value: 'ccc', 670 | children: [ 671 | { 672 | title: 'Child Node1', 673 | value: '0-1-0', 674 | key: '0-1-0', 675 | }, 676 | { 677 | title: 'Child Node2', 678 | value: '0-1-1', 679 | key: '0-1-1', 680 | }, 681 | { 682 | title: 'Child Node3', 683 | value: '0-1-2', 684 | key: '0-1-2', 685 | }, 686 | ], 687 | }, 688 | ]) 689 | } else if (linkage === 2) { 690 | resolve([ 691 | { 692 | label: 'CCC', 693 | value: 'ccc', 694 | children: [ 695 | { 696 | title: 'Child Node1', 697 | value: '0-0-0', 698 | key: '0-0-0', 699 | }, 700 | { 701 | title: 'Child Node2', 702 | value: '0-0-1', 703 | key: '0-0-1', 704 | }, 705 | { 706 | title: 'Child Node3', 707 | value: '0-0-2', 708 | key: '0-0-2', 709 | }, 710 | ], 711 | }, 712 | { 713 | label: 'DDD', 714 | value: 'ddd', 715 | children: [ 716 | { 717 | title: 'Child Node1', 718 | value: '0-1-0', 719 | key: '0-1-0', 720 | }, 721 | { 722 | title: 'Child Node2', 723 | value: '0-1-1', 724 | key: '0-1-1', 725 | }, 726 | { 727 | title: 'Child Node3', 728 | value: '0-1-2', 729 | key: '0-1-2', 730 | }, 731 | ], 732 | }, 733 | ]) 734 | } 735 | }, 1500) 736 | }) 737 | }) 738 | }, 739 | }) 740 | 741 | export default () => ( 742 | <FormProvider form={form}> 743 | <Field 744 | name="linkage" 745 | title="Linkage selection box" 746 | dataSource={[ 747 | { label: 'Request 1', value: 1 }, 748 | { label: 'Request 2', value: 2 }, 749 | ]} 750 | decorator={[FormItem]} 751 | component={[ 752 | Select, 753 | { 754 | style: { 755 | width: 200, 756 | }, 757 | }, 758 | ]} 759 | /> 760 | <Field 761 | name="select" 762 | title="Asynchronous select box" 763 | decorator={[FormItem]} 764 | component={[ 765 | TreeSelect, 766 | { 767 | style: { 768 | width: 200, 769 | }, 770 | }, 771 | ]} 772 | /> 773 | <FormButtonGroup> 774 | <Submit onSubmit={console.log}>Submit</Submit> 775 | </FormButtonGroup> 776 | </FormProvider> 777 | ) 778 | ``` 779 | 780 | ## API 781 | 782 | Reference https://ant.design/components/tree-select-cn/ 783 | ```