This is page 24 of 35. Use http://codebase.md/alibaba/formily?lines=false&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/antd/docs/components/TreeSelect.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # TreeSelect > 树选择器 ## Markup Schema 同步数据源案例 ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TreeSelect, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Number name="select" title="选择框" x-decorator="FormItem" x-component="TreeSelect" enum={[ { label: '选项1', value: 1, children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: '选项2', value: 2, children: [ { title: 'Child Node3', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node4', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node5', value: '0-1-2', key: '0-1-2', }, ], }, ]} x-component-props={{ style: { width: 200, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Markup Schema 异步联动数据源案例 ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/antd' import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { action } from '@formily/reactive' const SchemaField = createSchemaField({ components: { Select, TreeSelect, FormItem, }, }) const useAsyncDataSource = ( pattern: FormPathPattern, service: (field: Field) => Promise<{ label: string; value: any }[]> ) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) }) }, }) export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Number name="linkage" title="联动选择框" x-decorator="FormItem" x-component="Select" enum={[ { label: '发请求1', value: 1 }, { label: '发请求2', value: 2 }, ]} x-component-props={{ style: { width: 200, }, }} /> <SchemaField.String name="select" title="异步选择框" x-decorator="FormItem" x-component="TreeSelect" x-component-props={{ style: { width: 200, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 同步数据源案例 ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TreeSelect, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { select: { type: 'string', title: '选择框', 'x-decorator': 'FormItem', 'x-component': 'TreeSelect', enum: [ { label: '选项1', value: 1, children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: '选项2', value: 2, children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ], 'x-component-props': { style: { width: 200, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 异步联动数据源案例 ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { action } from '@formily/reactive' const SchemaField = createSchemaField({ components: { Select, TreeSelect, FormItem, }, }) const loadData = async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) } const useAsyncDataSource = (service) => (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) } const form = createForm() const schema = { type: 'object', properties: { linkage: { type: 'string', title: '联动选择框', enum: [ { label: '发请求1', value: 1 }, { label: '发请求2', value: 2 }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', 'x-component-props': { style: { width: 200, }, }, }, select: { type: 'string', title: '异步选择框', 'x-decorator': 'FormItem', 'x-component': 'TreeSelect', 'x-component-props': { style: { width: 200, }, }, 'x-reactions': ['{{useAsyncDataSource(loadData)}}'], }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 同步数据源案例 ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="select" title="选择框" dataSource={[ { label: '选项1', value: 1, children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: '选项2', value: 2, children: [ { title: 'Child Node3', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node4', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node5', value: '0-1-2', key: '0-1-2', }, ], }, ]} decorator={[FormItem]} component={[TreeSelect]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 异步联动数据源案例 ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/antd' import { createForm, onFieldReact, FormPathPattern, Field as FieldType, } from '@formily/core' import { FormProvider, Field } from '@formily/react' import { action } from '@formily/reactive' const useAsyncDataSource = ( pattern: FormPathPattern, service: (field: FieldType) => Promise<{ label: string; value: any }[]> ) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) }) }, }) export default () => ( <FormProvider form={form}> <Field name="linkage" title="联动选择框" dataSource={[ { label: '发请求1', value: 1 }, { label: '发请求2', value: 2 }, ]} decorator={[FormItem]} component={[ Select, { style: { width: 200, }, }, ]} /> <Field name="select" title="异步选择框" decorator={[FormItem]} component={[ TreeSelect, { style: { width: 200, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API 参考 https://ant.design/components/tree-select-cn/ ``` -------------------------------------------------------------------------------- /packages/path/src/__tests__/parser.spec.ts: -------------------------------------------------------------------------------- ```typescript import expect from 'expect' import { Parser } from '../parser' import { Path } from '../' const parse = (string: string, json: any, index: number) => { test('test ' + string + ` : ${index}`, () => { const parser = new Parser(string) expect(parser.parse()).toEqual(json) }) } const batchTest = (obj) => { let i = 0 for (let key in obj) { i++ parse(key, obj[key], i) } } test('relative', () => { const parser = new Parser('..[ + 1 ].dd.bb', new Path(['aa', '1', 'cc'])) const parser2 = new Parser('.ee', new Path(['aa', '1', 'cc'])) const parser3 = new Parser('.', new Path(['aa', '1', 'cc'])) const parser4 = new Parser('..', new Path(['aa', '1', 'cc'])) const parser5 = new Parser('.[].dd', new Path(['aa', '1'])) const parser6 = new Parser('.[].[]', new Path(['aa', '1'])) const parser7 = new Parser('.[].[aa]', new Path(['aa', '1'])) parser2.parse() parser3.parse() parser4.parse() parser5.parse() parser6.parse() parser7.parse() expect(parser.parse()).toEqual({ type: 'Identifier', value: 'aa', after: { type: 'DotOperator', after: { type: 'DestructorExpression', value: { type: 'ArrayPattern', elements: [ { type: 'Identifier', value: '+', after: { type: 'Identifier', value: '1', }, }, ], }, source: '2', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'dd', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'bb', }, }, }, }, }, }, }) expect(parser.data.segments).toEqual(['aa', '2', 'dd', 'bb']) expect(parser2.data.segments).toEqual(['aa', '1', 'ee']) expect(parser3.data.segments).toEqual(['aa', '1']) expect(parser4.data.segments).toEqual(['aa']) expect(parser5.data.segments).toEqual(['aa', '1', 'dd']) expect(parser6.data.segments).toEqual(['aa', '1', '[]']) expect(parser7.data.segments).toEqual(['aa', '1', '[aa]']) }) test('calculate', () => { const parser0 = new Parser('..[+].dd.bb', new Path(['aa', '1', 'cc'])) parser0.parse() expect(parser0.data.segments).toEqual(['aa', '2', 'dd', 'bb']) const parser = new Parser('..[ -1 ].dd.bb', new Path(['aa', '1', 'cc'])) parser.parse() expect(parser.data.segments).toEqual(['aa', '0', 'dd', 'bb']) // TODO support // const parser2 = new Parser('..[ *2 ].dd.bb', new Path(['aa', '2', 'cc'])) // parser2.parse() // expect(parser2.data.segments).toEqual(['aa', '4', 'dd', 'bb']) const parser3 = new Parser('..[ /2 ].dd.bb', new Path(['aa', '2', 'cc'])) parser3.parse() expect(parser3.data.segments).toEqual(['aa', '1', 'dd', 'bb']) const parser4 = new Parser('..[ +1 ].dd.bb', new Path(['aa', 'a', 'cc'])) parser4.parse() expect(parser4.data.segments).toEqual(['aa', 'a1', 'dd', 'bb']) const parser5 = new Parser('..[ -1 ].dd.bb', new Path(['aa', 'a', 'cc'])) parser5.parse() expect(parser5.data.segments).toEqual(['aa', 'NaN', 'dd', 'bb']) // TODO support // const parser6 = new Parser('..[ *1 ].dd.bb', new Path(['aa', 'a', 'cc'])) // parser6.parse() // expect(parser6.data.segments).toEqual(['aa', 'NaN', 'dd', 'bb']) const parser7 = new Parser('..[ /1 ].dd.bb', new Path(['aa', 'a', 'cc'])) parser7.parse() expect(parser7.data.segments).toEqual(['aa', 'NaN', 'dd', 'bb']) const parser8 = new Parser('..[1].dd.bb', new Path(['aa', '1', 'cc'])) parser8.parse() expect(parser8.data.segments).toEqual(['aa', '2', 'dd', 'bb']) const parser9 = new Parser('') parser9.next() expect(parser9.parseObjectProperties()).toEqual([]) }) test('parser unexpected', () => { const parser = new Parser('array[]') expect(() => parser.parse()).toThrowError() const parser2 = new Parser('array[0.') expect(() => parser2.parse()).toThrowError() const parser3 = new Parser('.[+]', new Path('*.1.cc')) expect(() => parser3.parse()).toThrowError() const parser4 = new Parser('[:,4]') expect(() => parser4.parse()).toThrowError() }) test('tokenizer', () => { const originFromCharCode = String.fromCharCode String.fromCharCode = null const parser = new Parser('aa.bb') parser.parse() expect(parser.data.segments).toEqual(['aa', 'bb']) const parser2 = new Parser('array.0.[aa,bb]') parser2.parse() expect(parser2.data.segments).toEqual(['array', '0', '[aa,bb]']) String.fromCharCode = originFromCharCode const char13 = String.fromCharCode(13) const parser3 = new Parser(`${char13} aa.bb`) parser3.parse() const char11 = String.fromCharCode(11) const parser4 = new Parser(`${char11} aa.bb`) parser4.parse() const parser5 = new Parser('') parser5.next() parser5.parse() expect(parser5.data.segments).toEqual([]) const char10 = String.fromCharCode(10) const parser6 = new Parser(`{ c${char13}${char10}: kk, d : mm }`) parser6.parse() expect(parser6.data.segments).toEqual(['{c:kk,d:mm}']) const parser7 = new Parser(`{ c${char13}${char11}: kk, d : mm }`) parser7.parse() expect(parser7.data.segments).toEqual(['{c:kk,d:mm}']) const parser8 = new Parser(`\\name`) parser8.state.pos++ parser8.parse() expect(parser8.data.segments).toEqual(['name']) const parser9 = new Parser(`[a,{b}]`) parser9.parse() expect(parser9.data.segments).toEqual(['[a,{b}]']) const parser10 = new Parser(`*(a.b.c.*(aa,bb))`) parser10.parse() expect(parser10.data.segments).toEqual([]) const parser11 = new Parser(`{a,[b,{c}]. }`) expect(() => parser11.parse()).toThrowError() const parser12 = new Parser(`*(a.*[1:3])`) parser12.parse() expect(parser12.data.segments).toEqual([]) const parser13 = new Parser(`*(a.*[1:3]])`) expect(() => parser13.parse()).toThrowError() }) batchTest({ '*': { type: 'WildcardOperator', }, 'a.b.c': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'c', }, }, }, }, }, 'a.b.*': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'WildcardOperator', }, }, }, }, }, 'a.b.*(111,222,aaa)': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'WildcardOperator', filter: { type: 'GroupExpression', value: [ { type: 'Identifier', value: '111', }, { type: 'Identifier', value: '222', }, { type: 'Identifier', value: 'aaa', }, ], }, }, }, }, }, }, 'a.b.*(!111,222,aaa)': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'WildcardOperator', filter: { type: 'GroupExpression', isExclude: true, value: [ { type: 'Identifier', value: '111', }, { type: 'Identifier', value: '222', }, { type: 'Identifier', value: 'aaa', }, ], }, }, }, }, }, }, 'a.b. * [ 11 : 22 ]': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'WildcardOperator', filter: { type: 'RangeExpression', start: { type: 'Identifier', value: '11', }, end: { type: 'Identifier', value: '22', }, }, }, }, }, }, }, 'a.b.*([[123123!,()]],[[aaa]])': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'WildcardOperator', filter: { type: 'GroupExpression', value: [ { type: 'IgnoreExpression', value: '123123!,()', }, { type: 'IgnoreExpression', value: 'aaa', }, ], }, }, }, }, }, }, 'a.b.*([[123123!,()]],aaa)': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'WildcardOperator', filter: { type: 'GroupExpression', value: [ { type: 'IgnoreExpression', value: '123123!,()', }, { type: 'Identifier', value: 'aaa', }, ], }, }, }, }, }, }, 'a.b.*(![[123123!,()]],aaa)': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'WildcardOperator', filter: { type: 'GroupExpression', value: [ { type: 'IgnoreExpression', value: '123123!,()', }, { type: 'Identifier', value: 'aaa', }, ], isExclude: true, }, }, }, }, }, }, 'a.b . * (![[123123!,()]],aaa,bbb)': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'WildcardOperator', filter: { type: 'GroupExpression', value: [ { type: 'IgnoreExpression', value: '123123!,()', }, { type: 'Identifier', value: 'aaa', }, { type: 'Identifier', value: 'bbb', }, ], isExclude: true, }, }, }, }, }, }, 'a.b.[[123123!,()]] ': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'IgnoreExpression', value: '123123!,()', }, }, }, }, }, [`a . b . [[123123!,()]] .aaaa`]: { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', after: { type: 'DotOperator', after: { type: 'IgnoreExpression', value: '123123!,()', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'aaaa', }, }, }, }, }, }, }, 'a.*(aaa.d.*(!sss),ddd,bbb).c.b': { type: 'Identifier', value: 'a', after: { type: 'DotOperator', after: { type: 'WildcardOperator', filter: { type: 'GroupExpression', value: [ { type: 'Identifier', value: 'aaa', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'd', after: { type: 'DotOperator', after: { type: 'WildcardOperator', filter: { type: 'GroupExpression', isExclude: true, value: [ { type: 'Identifier', value: 'sss', }, ], }, }, }, }, }, }, { type: 'Identifier', value: 'ddd', }, { type: 'Identifier', value: 'bbb', }, ], }, after: { type: 'DotOperator', after: { type: 'Identifier', value: 'c', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'b', }, }, }, }, }, }, }, 'aa.bb.cc.{aa,bb,cc:kk}': { type: 'Identifier', value: 'aa', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'bb', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'cc', after: { type: 'DotOperator', after: { type: 'DestructorExpression', value: { type: 'ObjectPattern', properties: [ { type: 'ObjectPatternProperty', key: { type: 'Identifier', value: 'aa' }, }, { type: 'ObjectPatternProperty', key: { type: 'Identifier', value: 'bb' }, }, { type: 'ObjectPatternProperty', key: { type: 'Identifier', value: 'cc' }, value: { type: 'Identifier', value: 'kk' }, }, ], }, source: '{aa,bb,cc:kk}', }, }, }, }, }, }, }, 'aa.bb.cc.[ [aa,bb,cc,[ [{aa:bb}] ]] ]': { type: 'Identifier', value: 'aa', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'bb', after: { type: 'DotOperator', after: { type: 'Identifier', value: 'cc', after: { type: 'DotOperator', after: { type: 'DestructorExpression', value: { type: 'ArrayPattern', elements: [ { type: 'ArrayPattern', elements: [ { type: 'Identifier', value: 'aa', }, { type: 'Identifier', value: 'bb', }, { type: 'Identifier', value: 'cc', }, { type: 'ArrayPattern', elements: [ { type: 'ArrayPattern', elements: [ { type: 'ObjectPattern', properties: [ { type: 'ObjectPatternProperty', key: { type: 'Identifier', value: 'aa', }, value: { type: 'Identifier', value: 'bb', }, }, ], }, ], }, ], }, ], }, ], }, source: '[[aa,bb,cc,[[{aa:bb}]]]]', }, }, }, }, }, }, }, }) ``` -------------------------------------------------------------------------------- /packages/next/docs/components/TreeSelect.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # TreeSelect > 树选择器 ## Markup Schema 同步数据源案例 ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TreeSelect, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Number name="select" label="选择框" x-decorator="FormItem" x-component="TreeSelect" enum={[ { label: '选项1', value: 1, children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: '选项2', value: 2, children: [ { label: 'Child Node3', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node4', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node5', value: '0-1-2', key: '0-1-2', }, ], }, ]} x-component-props={{ style: { width: 200, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Markup Schema 异步联动数据源案例 ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/next' import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { action } from '@formily/reactive' const SchemaField = createSchemaField({ components: { Select, TreeSelect, FormItem, }, }) const useAsyncDataSource = ( pattern: FormPathPattern, service: (field: Field) => Promise<{ label: string; value: any }[]> ) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) }) }, }) export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Number name="linkage" label="联动选择框" x-decorator="FormItem" x-component="Select" enum={[ { label: '发请求1', value: 1 }, { label: '发请求2', value: 2 }, ]} x-component-props={{ style: { width: 200, }, }} /> <SchemaField.String name="select" label="异步选择框" x-decorator="FormItem" x-component="TreeSelect" x-component-props={{ style: { width: 200, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 同步数据源案例 ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TreeSelect, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { select: { type: 'string', label: '选择框', 'x-decorator': 'FormItem', 'x-component': 'TreeSelect', enum: [ { label: '选项1', value: 1, children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: '选项2', value: 2, children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ], 'x-component-props': { style: { width: 200, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 异步联动数据源案例 ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { action } from '@formily/reactive' const SchemaField = createSchemaField({ components: { Select, TreeSelect, FormItem, }, }) const loadData = async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) } const useAsyncDataSource = (service) => (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) } const form = createForm() const schema = { type: 'object', properties: { linkage: { type: 'string', label: '联动选择框', enum: [ { label: '发请求1', value: 1 }, { label: '发请求2', value: 2 }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', 'x-component-props': { style: { width: 200, }, }, }, select: { type: 'string', label: '异步选择框', 'x-decorator': 'FormItem', 'x-component': 'TreeSelect', 'x-component-props': { style: { width: 200, }, }, 'x-reactions': ['{{useAsyncDataSource(loadData)}}'], }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 同步数据源案例 ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="select" label="选择框" dataSource={[ { label: '选项1', value: 1, children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: '选项2', value: 2, children: [ { label: 'Child Node3', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node4', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node5', value: '0-1-2', key: '0-1-2', }, ], }, ]} decorator={[FormItem]} component={[ TreeSelect, { style: { width: 200, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 异步联动数据源案例 ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/next' import { createForm, onFieldReact, FormPathPattern, FieldType, } from '@formily/core' import { FormProvider, Field } from '@formily/react' import { action } from '@formily/reactive' const useAsyncDataSource = ( pattern: FormPathPattern, service: (field: FieldType) => Promise<{ label: string; value: any }[]> ) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) }) }, }) export default () => ( <FormProvider form={form}> <Field name="linkage" label="联动选择框" dataSource={[ { label: '发请求1', value: 1 }, { label: '发请求2', value: 2 }, ]} decorator={[FormItem]} component={[ Select, { style: { width: 200, }, }, ]} /> <Field name="select" label="异步选择框" decorator={[FormItem]} component={[ TreeSelect, { style: { width: 200, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API 参考 https://fusion.design/pc/component/basic/tree-select ``` -------------------------------------------------------------------------------- /packages/vue/src/__tests__/schema.markup.spec.ts: -------------------------------------------------------------------------------- ```typescript import { createForm } from '@formily/core' import { useFieldSchema, useField, Schema } from '../' import { FormProvider, RecursionField, createSchemaField, } from '../vue2-components' import { render } from '@testing-library/vue' import { mount, createLocalVue } from '@vue/test-utils' import Vue, { CreateElement } from 'vue' import { defineComponent, h } from '@vue/composition-api' Vue.component('FormProvider', FormProvider) Vue.component('RecursionField', RecursionField) const Input = defineComponent({ props: ['value'], setup(props, { attrs, listeners }) { return () => { return h('input', { attrs: { ...attrs, value: props.value, 'data-testid': 'input', }, on: { ...listeners, input: listeners.change, }, }) } }, }) describe('markup schema field', () => { test('string', () => { const form = createForm() const { SchemaField, SchemaStringField } = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render({ components: { SchemaField, SchemaStringField }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaStringField x-component="Input" /> </SchemaField> </FormProvider>`, }) expect(queryByTestId('input')).toBeVisible() }) test('boolean', () => { const form = createForm() const { SchemaField, SchemaBooleanField } = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render({ components: { SchemaField, SchemaBooleanField }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaBooleanField x-component="Input" /> </SchemaField> </FormProvider>`, }) expect(queryByTestId('input')).toBeVisible() }) test('number', () => { const form = createForm() const { SchemaField, SchemaNumberField } = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render({ components: { SchemaField, SchemaNumberField }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaNumberField x-component="Input" /> </SchemaField> </FormProvider>`, }) expect(queryByTestId('input')).toBeVisible() }) test('date', () => { const form = createForm() const { SchemaField, SchemaDateField } = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render({ components: { SchemaField, SchemaDateField }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaDateField x-component="Input" /> </SchemaField> </FormProvider>`, }) expect(queryByTestId('input')).toBeVisible() }) test('datetime', () => { const form = createForm() const { SchemaField, SchemaDateTimeField } = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render({ components: { SchemaField, SchemaDateTimeField }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaDateTimeField x-component="Input" /> </SchemaField> </FormProvider>`, }) expect(queryByTestId('input')).toBeVisible() }) test('void', () => { const form = createForm() const VoidComponent = { render(h: CreateElement) { return h( 'div', { attrs: { 'data-testid': 'void-component' } }, this.$slots.default ) }, } const { SchemaField, SchemaVoidField } = createSchemaField({ components: { VoidComponent, }, }) const { queryByTestId } = render({ components: { SchemaField, SchemaVoidField }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaVoidField x-component="VoidComponent" /> </SchemaField> </FormProvider>`, }) expect(queryByTestId('void-component')).toBeVisible() }) test('array', () => { const form = createForm() const components = createSchemaField({ components: { Input, }, }) render({ components: { ...components }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaArrayField> <SchemaObjectField> <SchemaStringField x-component="Input" /> </SchemaObjectField> <SchemaVoidField /> </SchemaArrayField> </SchemaField> </FormProvider>`, }) }) test('other', () => { const form = createForm() const components = createSchemaField({ components: { Input, }, }) render({ components: { ...components }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaMarkupField type="other"> <SchemaMarkupField /> </SchemaMarkupField> </SchemaField> </FormProvider>`, }) }) test('no parent', () => { const form = createForm() const components = createSchemaField({ components: { Input, }, }) render({ components: { ...components }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaMarkupField type="other"> <SchemaMarkupField /> </SchemaMarkupField> </FormProvider>`, }) }) }) describe('recursion field', () => { test('onlyRenderProperties', () => { const form = createForm() const CustomObject = defineComponent({ setup() { const schemaRef = useFieldSchema() return () => { return h('div', { attrs: { 'data-testid': 'object' } }, [ h('RecursionField', { props: { schema: schemaRef.value } }), ]) } }, }) const CustomObject2 = defineComponent({ setup() { const fieldRef = useField() const schemaRef = useFieldSchema() return () => { const schema = schemaRef.value const field = fieldRef.value return h('div', { attrs: { 'data-testid': 'only-properties' } }, [ h('RecursionField', { props: { name: schema.name, basePath: field.address, schema, onlyRenderProperties: true, }, }), ]) } }, }) const components = createSchemaField({ components: { Input, CustomObject, CustomObject2, }, }) const { queryAllByTestId } = render({ components: components, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaObjectField x-component="CustomObject"> <SchemaStringField x-component="Input" /> </SchemaObjectField> <SchemaObjectField x-component="CustomObject2"> <SchemaStringField x-component="Input" /> </SchemaObjectField> <SchemaVoidField x-component="CustomObject2"> <SchemaStringField x-component="Input" /> </SchemaVoidField> </SchemaField> </FormProvider>`, }) expect(queryAllByTestId('input').length).toEqual(3) expect(queryAllByTestId('object').length).toEqual(1) expect(queryAllByTestId('only-properties').length).toEqual(2) }) test('mapProperties', () => { const form = createForm() const CustomObject = defineComponent({ setup() { const schemaRef = useFieldSchema() return () => { return h('div', { attrs: { 'data-testid': 'object' } }, [ h('RecursionField', { props: { schema: schemaRef.value, mapProperties: (schema) => { schema.default = '123' return schema }, }, }), ]) } }, }) const CustomObject2 = defineComponent({ setup() { const schemaRef = useFieldSchema() return () => { const schema = schemaRef.value return h('div', { attrs: { 'data-testid': 'object' } }, [ h('RecursionField', { props: { schema, mapProperties: () => { return null }, }, }), ]) } }, }) const components = createSchemaField({ components: { Input, CustomObject, CustomObject2, }, }) const { queryAllByTestId } = render({ components: components, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaObjectField x-component="CustomObject"> <SchemaStringField x-component="Input" /> </SchemaObjectField> <SchemaObjectField x-component="CustomObject2"> <SchemaStringField x-component="Input" /> </SchemaObjectField> </SchemaField> </FormProvider>`, }) expect(queryAllByTestId('input').length).toEqual(2) expect(queryAllByTestId('input')[0].getAttribute('value')).toEqual('123') expect(queryAllByTestId('input')[1].getAttribute('value')).toBeFalsy() }) test('filterProperties', () => { const form = createForm() const CustomObject = defineComponent({ setup() { const schemaRef = useFieldSchema() return () => { return h('div', { attrs: { 'data-testid': 'object' } }, [ h('RecursionField', { props: { schema: schemaRef.value, filterProperties: (schema: Schema) => { if (schema['x-component'] === 'Input') return false return true }, }, }), ]) } }, }) const CustomObject2 = defineComponent({ setup() { const schemaRef = useFieldSchema() return () => { return h('div', { attrs: { 'data-testid': 'object' } }, [ h('RecursionField', { props: { schema: schemaRef.value, filterProperties: (schema: Schema) => { if (schema['x-component'] === 'Input') return return true }, }, }), ]) } }, }) const components = createSchemaField({ components: { Input, CustomObject, CustomObject2, }, }) const { queryAllByTestId } = render({ components: components, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaObjectField x-component="CustomObject"> <SchemaStringField x-component="Input" /> </SchemaObjectField> <SchemaObjectField x-component="CustomObject2"> <SchemaStringField x-component="Input" /> </SchemaObjectField> </SchemaField> </FormProvider>`, }) expect(queryAllByTestId('input').length).toEqual(1) expect(queryAllByTestId('object').length).toEqual(2) }) test('onlyRenderSelf', () => { const form = createForm() const CustomObject = defineComponent({ setup() { const schemaRef = useFieldSchema() return () => { return h('div', { attrs: { 'data-testid': 'object' } }, [ h('RecursionField', { props: { schema: schemaRef.value, onlyRenderSelf: true, }, }), ]) } }, }) const components = createSchemaField({ components: { Input, CustomObject, }, }) const { queryAllByTestId } = render({ components: components, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaObjectField x-component="CustomObject"> <SchemaStringField x-component="Input" /> </SchemaObjectField> </SchemaField> </FormProvider>`, }) expect(queryAllByTestId('input').length).toEqual(0) expect(queryAllByTestId('object').length).toEqual(1) }) test('illegal schema', () => { const form = createForm() const CustomObject = defineComponent({ setup() { return () => { return h('div', { attrs: { 'data-testid': 'object' } }, [ h('RecursionField', { props: { schema: null, }, }), ]) } }, }) const CustomObject2 = defineComponent({ setup() { return () => { return h('div', { attrs: { 'data-testid': 'object' } }, [ h('RecursionField', { props: { schema: {}, }, }), ]) } }, }) const components = createSchemaField({ components: { Input, CustomObject, CustomObject2, }, }) const { queryByTestId } = render({ components: components, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaObjectField x-component="CustomObject"> <SchemaStringField x-component="Input" /> </SchemaObjectField> <SchemaObjectField x-component="CustomObject2"> <SchemaStringField x-component="Input" /> </SchemaObjectField> </SchemaField> </FormProvider>`, }) expect(queryByTestId('input')).toBeNull() }) test('schema reactions', async () => { const div = document.createElement('div') document.body.appendChild(div) const form = createForm() const components = createSchemaField({ components: { Input, }, }) const localVue = createLocalVue() localVue.component('FormProvider', FormProvider) const TestComponent = { components: components, data() { return { form, reactions: [ { when: '{{$form.values.aaa === "123"}}', fulfill: { state: { visible: true, }, }, otherwise: { state: { visible: false, }, }, }, { when: '{{$self.value === "123"}}', target: 'ccc', fulfill: { schema: { 'x-visible': true, }, }, otherwise: { schema: { 'x-visible': false, }, }, }, ], } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaStringField name="aaa" x-component="Input" :x-component-props="{ 'class': 'aaa', }" /> <SchemaStringField name="bbb" x-component="Input" :x-component-props="{ 'class': 'bbb', }" :x-reactions="reactions" /> <SchemaStringField name="ccc" x-component="Input" :x-component-props="{ 'class': 'ccc', }" /> </SchemaField> </FormProvider>`, } as any const wrapper = mount(TestComponent, { // attachTo: div, attachToDocument: true, localVue, }) expect(wrapper.find('.bbb').exists()).toBeFalsy() wrapper.find('.aaa').setValue('123') expect(form.query('aaa').get('value')).toEqual('123') await wrapper.vm.$forceUpdate() expect(wrapper.find('.bbb').exists()).toBeTruthy() expect(wrapper.find('.ccc').exists()).toBeFalsy() wrapper.find('.bbb').setValue('123') expect(form.query('bbb').get('value')).toEqual('123') await wrapper.vm.$forceUpdate() expect(wrapper.find('.ccc').exists()).toBeTruthy() wrapper.destroy() }) test('void field children', () => { const form = createForm() const VoidComponent = { render(h: CreateElement) { return h('div', this.$slots.default || 'placeholder') }, } const { SchemaField, SchemaVoidField } = createSchemaField({ components: { VoidComponent, }, }) const { queryByTestId } = render({ components: { SchemaField, SchemaVoidField }, data() { return { form, } }, template: `<FormProvider :form="form"> <SchemaField> <SchemaVoidField x-component="VoidComponent" :x-component-props="{ 'data-testid': 'void-component-1' }" /> <SchemaVoidField x-component="VoidComponent" :x-component-props="{ 'data-testid': 'void-component-2' }" x-content="content" /> </SchemaField> </FormProvider>`, }) expect(queryByTestId('void-component-1').textContent).toBe('placeholder') expect(queryByTestId('void-component-2').textContent).toBe('content') }) }) ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/SelectTable.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # SelectTable > 表格选择组件 ## Markup Schema 单选案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, SelectTable, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.Object type="string" name="selectTable" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ bordered: false, mode: 'single', }} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} > <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormProvider> ) } ``` ## Markup Schema 筛选案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, SelectTable, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.Array type="array" name="selectTable" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ bordered: false, showSearch: true, optionAsValue: true, }} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} > <SchemaField.Object> <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField.Array> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormProvider> ) } ``` ## Markup Schema 异步数据源案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, SelectTable, }, }) const form = createForm() export default () => { const onSearch = (value) => { const field = form.query('selectTable').take() field.loading = true setTimeout(() => { field.setState({ dataSource: [ { key: '3', name: 'AAA' + value, description: 'aaa', }, { key: '4', name: 'BBB' + value, description: 'bbb', }, ], loading: false, }) }, 1500) } return ( <FormProvider form={form}> <SchemaField> <SchemaField.Object type="object" name="selectTable" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ showSearch: true, filterOption: false, onSearch, }} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} > <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormProvider> ) } ``` ## Markup Schema 阅读态案例 ```tsx import React from 'react' import { Form, FormItem, FormButtonGroup, Submit, SelectTable, } from '@formily/antd' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, SelectTable, }, }) const form = createForm() export default () => { return ( <Form form={form} layout="vertical"> <SchemaField> <SchemaField.Object title="单选" type="string" name="selectTable1" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ mode: 'single', }} default="1" enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} x-read-pretty={true} > <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> <SchemaField.Object title="单选 + optionAsValue" type="string" name="selectTable2" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ mode: 'single', optionAsValue: true, }} default={{ key: '1', name: '标题1', description: '描述1' }} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} x-read-pretty={true} > <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> <SchemaField.Array title="多选" type="array" name="selectTable3" x-decorator="FormItem" x-component="SelectTable" default={['1', '3']} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, { key: '3', name: '标题3', description: '描述3' }, ]} x-read-pretty={true} > <SchemaField.Object> <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField.Array> <SchemaField.Array title="多选 + optionAsValue" type="array" name="selectTable4" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ optionAsValue: true, }} default={[ { key: '1', name: '标题1', description: '描述1' }, { key: '3', name: '标题3', description: '描述3' }, ]} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, { key: '3', name: '标题3', description: '描述3' }, ]} x-read-pretty={true} > <SchemaField.Object> <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField.Array> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </Form> ) } ``` ## JSON Schema 多选案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { SelectTable, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { selectTable: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'SelectTable', 'x-component-props': { bordered: false, mode: 'multiple', }, enum: [ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ], properties: { name: { title: '标题', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '40%', }, }, description: { title: '描述', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '60%', }, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 自定义筛选案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { SelectTable, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { selectTable: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'SelectTable', 'x-component-props': { bordered: false, showSearch: true, primaryKey: 'key', isTree: true, filterOption: (input, option) => option.description.toLowerCase().indexOf(input.toLowerCase()) >= 0, filterSort: (optionA, optionB) => optionA.description .toLowerCase() .localeCompare(optionB.description.toLowerCase()), optionAsValue: true, rowSelection: { checkStrictly: false, }, }, enum: [ { key: '1', name: '标题1', description: 'A-描述' }, { key: '2', name: '标题2', description: 'X-描述', children: [ { key: '2-1', name: '标题2-1', description: 'Y-描述', children: [ { key: '2-1-1', name: '标题2-1-1', description: 'Z-描述' }, ], }, { key: '2-2', name: '标题2-2', description: 'YY-描述', }, ], }, { key: '3', name: '标题3', description: 'C-描述' }, ], properties: { name: { title: '标题', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '40%', }, }, description: { title: '描述', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '60%', }, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 异步数据源案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { SelectTable, FormItem, }, }) const loadData = async (value) => { return new Promise((resolve) => { setTimeout(() => { resolve([ { key: '3', name: 'AAA' + value, description: 'aaa' }, { key: '4', name: 'BBB' + value, description: 'bbb' }, ]) }, 1500) }) } const useAsyncDataSource = (service, field) => (value) => { field.loading = true service(value).then((data) => { field.setState({ dataSource: data, loading: false, }) }) } const form = createForm() const schema = { type: 'object', properties: { selectTable: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'SelectTable', 'x-component-props': { showSearch: true, filterOption: false, onSearch: '{{useAsyncDataSource(loadData,$self)}}', }, enum: [ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ], properties: { name: { title: '标题', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '40%', }, }, description: { title: '描述', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '60%', }, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="SelectTable" dataSource={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} decorator={[FormItem]} component={[ SelectTable, { columns: [ { dataIndex: 'name', title: '标题' }, { dataIndex: 'description', title: '描述' }, ], }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API ### SelectTable | 属性名 | 类型 | 描述 | 默认值 | | ------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------ | | mode | `'multiple' \| 'single'` | 设置 SelectTable 模式为单选或多选 | `'multiple'` | | valueType | `'all' \| 'parent' \| 'child' \| 'path'` | 返回值类型,checkStrictly 设置为 `false` 时有效 | `'all'` | | optionAsValue | boolean | 使用表格行数据作为值,valueType 值为 `'path'` 时无效 | false | | showSearch | boolean | 是否显示搜索组件 | false | | searchProps | object | Search 组件属性 | - | | primaryKey | `string \| (record) => string` | 表格行 key 的取值 | `'key'` | | filterOption | `boolean \| (inputValue, option) => boolean` | 是否根据输入项进行筛选。当其为一个函数时,会接收 inputValue option 两个参数,当 option 符合筛选条件时,应返回 true,反之则返回 false | true | | filterSort | (optionA, optionB) => number | 搜索时对筛选结果项的排序函数, 类似 Array.sort 里的 compareFunction | - | | onSearch | 文本框值变化时回调 | (inputValue) => void | - | 参考 https://ant.design/components/table-cn/ ### rowSelection | 属性名 | 类型 | 描述 | 默认值 | | ------------- | ------- | ------------------------------------------------------------ | ------ | | checkStrictly | boolean | checkable 状态下节点选择完全受控(父子数据选中状态不再关联) | true | 参考 https://ant.design/components/table/#rowSelection ### SelectTable.Column 参考 https://ant.design/components/table-cn/ Table.Column 属性 ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/TreeSelect.md: -------------------------------------------------------------------------------- ```markdown # TreeSelect > Tree selector ## Markup Schema synchronization data source case ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TreeSelect, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Number name="select" title="select box" x-decorator="FormItem" x-component="TreeSelect" enum={[ { label: 'Option 1', value: 1, children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'Option 2', value: 2, children: [ { title: 'Child Node3', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node4', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node5', value: '0-1-2', key: '0-1-2', }, ], }, ]} x-component-props={{ style: { width: 200, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Markup Schema Asynchronous Linkage Data Source Case ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/antd' import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { action } from '@formily/reactive' const SchemaField = createSchemaField({ components: { Select, TreeSelect, FormItem, }, }) const useAsyncDataSource = ( pattern: FormPathPattern, service: (field: Field) => Promise<{ label: string; value: any }[]> ) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) }) }, }) export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Number name="linkage" title="Linkage selection box" x-decorator="FormItem" x-component="Select" enum={[ { label: 'Request 1', value: 1 }, { label: 'Request 2', value: 2 }, ]} x-component-props={{ style: { width: 200, }, }} /> <SchemaField.String name="select" title="Asynchronous select box" x-decorator="FormItem" x-component="TreeSelect" x-component-props={{ style: { width: 200, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema synchronization data source case ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TreeSelect, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { select: { type: 'string', title: 'Select box', 'x-decorator': 'FormItem', 'x-component': 'TreeSelect', enum: [ { label: 'Option 1', value: 1, children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'Option 2', value: 2, children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ], 'x-component-props': { style: { width: 200, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema asynchronous linkage data source case ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { action } from '@formily/reactive' const SchemaField = createSchemaField({ components: { Select, TreeSelect, FormItem, }, }) const loadData = async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) } const useAsyncDataSource = (service) => (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) } const form = createForm() const schema = { type: 'object', properties: { linkage: { type: 'string', title: 'Linkage selection box', enum: [ { label: 'Request 1', value: 1 }, { label: 'Request 2', value: 2 }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', 'x-component-props': { style: { width: 200, }, }, }, select: { type: 'string', title: 'Asynchronous selection box', 'x-decorator': 'FormItem', 'x-component': 'TreeSelect', 'x-component-props': { style: { width: 200, }, }, 'x-reactions': ['{{useAsyncDataSource(loadData)}}'], }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Pure JSX synchronization data source case ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="select" title="select box" dataSource={[ { label: 'Option 1', value: 1, children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'Option 2', value: 2, children: [ { title: 'Child Node3', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node4', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node5', value: '0-1-2', key: '0-1-2', }, ], }, ]} decorator={[FormItem]} component={[TreeSelect]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Pure JSX asynchronous linkage data source case ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/antd' import { createForm, onFieldReact, FormPathPattern, Field as FieldType, } from '@formily/core' import { FormProvider, Field } from '@formily/react' import { action } from '@formily/reactive' const useAsyncDataSource = ( pattern: FormPathPattern, service: (field: FieldType) => Promise<{ label: string; value: any }[]> ) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { title: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { title: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { title: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { title: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { title: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { title: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) }) }, }) export default () => ( <FormProvider form={form}> <Field name="linkage" title="Linkage selection box" dataSource={[ { label: 'Request 1', value: 1 }, { label: 'Request 2', value: 2 }, ]} decorator={[FormItem]} component={[ Select, { style: { width: 200, }, }, ]} /> <Field name="select" title="Asynchronous select box" decorator={[FormItem]} component={[ TreeSelect, { style: { width: 200, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API Reference https://ant.design/components/tree-select-cn/ ``` -------------------------------------------------------------------------------- /packages/next/docs/components/SelectTable.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # SelectTable > 表格选择组件 ## Markup Schema 单选案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, SelectTable, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.Object type="string" name="selectTable" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ hasBorder: false, mode: 'single', }} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} > <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormProvider> ) } ``` ## Markup Schema 筛选案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, SelectTable, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.Array type="array" name="selectTable" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ hasBorder: false, showSearch: true, optionAsValue: true, }} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} > <SchemaField.Object> <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField.Array> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormProvider> ) } ``` ## Markup Schema 异步数据源案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, SelectTable, }, }) const form = createForm() export default () => { const onSearch = (value) => { const field = form.query('selectTable').take() field.loading = true setTimeout(() => { field.setState({ dataSource: [ { key: '3', name: 'AAA' + value, description: 'aaa', }, { key: '4', name: 'BBB' + value, description: 'bbb', }, ], loading: false, }) }, 1500) } return ( <FormProvider form={form}> <SchemaField> <SchemaField.Object type="object" name="selectTable" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ hasBorder: false, showSearch: true, filterOption: false, onSearch, }} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} > <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormProvider> ) } ``` ## Markup Schema 阅读态案例 ```tsx import React from 'react' import { Form, FormItem, FormButtonGroup, Submit, SelectTable, } from '@formily/next' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, SelectTable, }, }) const form = createForm() export default () => { return ( <Form form={form} layout="vertical"> <SchemaField> <SchemaField.Object title="单选" type="string" name="selectTable1" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ hasBorder: false, mode: 'single', }} default="1" enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} x-read-pretty={true} > <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> <SchemaField.Object title="单选 + optionAsValue" type="string" name="selectTable2" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ hasBorder: false, mode: 'single', optionAsValue: true, }} default={{ key: '1', name: '标题1', description: '描述1' }} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} x-read-pretty={true} > <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> <SchemaField.Array title="多选" type="array" name="selectTable3" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ hasBorder: false, }} default={['1', '3']} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, { key: '3', name: '标题3', description: '描述3' }, ]} x-read-pretty={true} > <SchemaField.Object> <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField.Array> <SchemaField.Array title="多选 + optionAsValue" type="array" name="selectTable4" x-decorator="FormItem" x-component="SelectTable" x-component-props={{ hasBorder: false, optionAsValue: true, }} default={[ { key: '1', name: '标题1', description: '描述1' }, { key: '3', name: '标题3', description: '描述3' }, ]} enum={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, { key: '3', name: '标题3', description: '描述3' }, ]} x-read-pretty={true} > <SchemaField.Object> <SchemaField.Void name="name" title="标题" x-component="SelectTable.Column" /> <SchemaField.Void name="description" title="描述" x-component="SelectTable.Column" /> </SchemaField.Object> </SchemaField.Array> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </Form> ) } ``` ## JSON Schema 多选案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { SelectTable, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { selectTable: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'SelectTable', 'x-component-props': { hasBorder: false, mode: 'multiple', }, enum: [ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ], properties: { name: { title: '标题', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '40%', }, }, description: { title: '描述', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '60%', }, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 自定义筛选案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { SelectTable, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { selectTable: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'SelectTable', 'x-component-props': { hasBorder: false, showSearch: true, primaryKey: 'key', isTree: true, filterOption: (input, option) => option.description.toLowerCase().indexOf(input.toLowerCase()) >= 0, filterSort: (optionA, optionB) => optionA.description .toLowerCase() .localeCompare(optionB.description.toLowerCase()), optionAsValue: true, rowSelection: { checkStrictly: false, }, }, enum: [ { key: '1', name: '标题1', description: 'A-描述' }, { key: '2', name: '标题2', description: 'X-描述', children: [ { key: '2-1', name: '标题2-1', description: 'Y-描述', children: [ { key: '2-1-1', name: '标题2-1-1', description: 'Z-描述' }, ], }, { key: '2-2', name: '标题2-2', description: 'YY-描述', }, ], }, { key: '3', name: '标题3', description: 'C-描述' }, ], properties: { name: { title: '标题', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '40%', }, }, description: { title: '描述', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '60%', }, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 异步数据源案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { SelectTable, FormItem, }, }) const loadData = async (value) => { return new Promise((resolve) => { setTimeout(() => { resolve([ { key: '3', name: 'AAA' + value, description: 'aaa' }, { key: '4', name: 'BBB' + value, description: 'bbb' }, ]) }, 1500) }) } const useAsyncDataSource = (service, field) => (value) => { field.loading = true service(value).then((data) => { field.setState({ dataSource: data, loading: false, }) }) } const form = createForm() const schema = { type: 'object', properties: { selectTable: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'SelectTable', 'x-component-props': { hasBorder: false, showSearch: true, filterOption: false, onSearch: '{{useAsyncDataSource(loadData,$self)}}', }, enum: [ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ], properties: { name: { title: '标题', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '40%', }, }, description: { title: '描述', type: 'string', 'x-component': 'SelectTable.Column', 'x-component-props': { width: '60%', }, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 案例 ```tsx import React from 'react' import { FormItem, FormButtonGroup, Submit, SelectTable } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="SelectTable" dataSource={[ { key: '1', name: '标题1', description: '描述1' }, { key: '2', name: '标题2', description: '描述2' }, ]} decorator={[FormItem]} component={[ SelectTable, { hasBorder: false, columns: [ { dataIndex: 'name', title: '标题' }, { dataIndex: 'description', title: '描述' }, ], }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API ### SelectTable | 属性名 | 类型 | 描述 | 默认值 | | ------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------ | | mode | `'multiple' \| 'single'` | 设置 SelectTable 模式为单选或多选 | `'multiple'` | | valueType | `'all' \| 'parent' \| 'child' \| 'path'` | 返回值类型,checkStrictly 设置为 `false` 时有效 | `'all'` | | optionAsValue | boolean | 使用表格行数据作为值,valueType 值为 `'path'` 时无效 | false | | showSearch | boolean | 是否显示搜索组件 | false | | searchProps | object | Search 组件属性 | - | | primaryKey | `string \| (record) => string` | 表格行 key 的取值 | `'key'` | | filterOption | `boolean \| (inputValue, option) => boolean` | 是否根据输入项进行筛选。当其为一个函数时,会接收 inputValue option 两个参数,当 option 符合筛选条件时,应返回 true,反之则返回 false | true | | filterSort | (optionA, optionB) => number | 搜索时对筛选结果项的排序函数, 类似 Array.sort 里的 compareFunction | - | | onSearch | 文本框值变化时回调 | (inputValue) => void | - | 参考 https://fusion.design/pc/component/basic/table ### rowSelection | 属性名 | 类型 | 描述 | 默认值 | | ------------- | ------- | ------------------------------------------------------------ | ------ | | checkStrictly | boolean | checkable 状态下节点选择完全受控(父子数据选中状态不再关联) | true | 参考 https://fusion.design/pc/component/basic/table rowSelection ### SelectTable.Column 参考 https://fusion.design/pc/component/basic/table Table.Column 属性 ```