This is page 32 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 -------------------------------------------------------------------------------- /docs/guide/scenes/login-register.md: -------------------------------------------------------------------------------- ```markdown # Log in&Sign up ## Log in #### Markup Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input, Password, Submit } from '@formily/antd' import { Tabs, Card } from 'antd' import * as ICONS from '@ant-design/icons' import { VerifyCode } from './VerifyCode' const normalForm = createForm({ validateFirst: true, }) const phoneForm = createForm({ validateFirst: true, }) const SchemaField = createSchemaField({ components: { FormItem, Input, Password, VerifyCode, }, scope: { icon(name) { return React.createElement(ICONS[name]) }, }, }) export default () => { return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card style={{ width: 400 }}> <Tabs style={{ overflow: 'visible', marginTop: -10 }}> <Tabs.TabPane key="1" tab="Password Login"> <Form form={normalForm} layout="vertical" size="large" onAutoSubmit={console.log} > <SchemaField> <SchemaField.String name="username" title="Username" required x-decorator="FormItem" x-component="Input" x-validator={{ required: true, }} x-component-props={{ prefix: "{{icon('UserOutlined')}}", }} /> <SchemaField.String name="password" title="Password" required x-decorator="FormItem" x-component="Password" x-component-props={{ prefix: "{{icon('LockOutlined')}}", }} /> </SchemaField> <Submit block size="large"> Log in </Submit> </Form> </Tabs.TabPane> <Tabs.TabPane key="2" tab="Mobile Login"> <Form form={phoneForm} layout="vertical" size="large" onAutoSubmit={console.log} > <SchemaField> <SchemaField.String name="phone" title="Phone Number" required x-validator="phone" x-decorator="FormItem" x-component="Input" x-component-props={{ prefix: "{{icon('PhoneOutlined')}}", }} /> <SchemaField.String name="verifyCode" title="Verification Code" required x-decorator="FormItem" x-component="VerifyCode" x-component-props={{ prefix: "{{icon('LockOutlined')}}", }} x-reactions={[ { dependencies: ['.phone#value', '.phone#valid'], fulfill: { state: { 'component[1].readyPost': '{{$deps[0] && $deps[1]}}', 'component[1].phoneNumber': '{{$deps[0]}}', }, }, }, ]} /> </SchemaField> <Submit block size="large"> Log in </Submit> </Form> </Tabs.TabPane> </Tabs> <div style={{ display: 'flex', justifyContent: 'space-between', }} > <a href="#Sign up">Sign up</a> <a href="#Forgot password">Forgot password?</a> </div> </Card> </div> ) } ``` #### JSON Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input, Password, Submit } from '@formily/antd' import { Tabs, Card } from 'antd' import * as ICONS from '@ant-design/icons' import { VerifyCode } from './VerifyCode' const normalForm = createForm({ validateFirst: true, }) const phoneForm = createForm({ validateFirst: true, }) const SchemaField = createSchemaField({ components: { FormItem, Input, Password, VerifyCode, }, scope: { icon(name) { return React.createElement(ICONS[name]) }, }, }) const normalSchema = { type: 'object', properties: { username: { type: 'string', title: 'Username', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { prefix: "{{icon('UserOutlined')}}", }, }, password: { type: 'string', title: 'Password', required: true, 'x-decorator': 'FormItem', 'x-component': 'Password', 'x-component-props': { prefix: "{{icon('LockOutlined')}}", }, }, }, } const phoneSchema = { type: 'object', properties: { phone: { type: 'string', title: 'Phone Number', required: true, 'x-validator': 'phone', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { prefix: "{{icon('PhoneOutlined')}}", }, }, verifyCode: { type: 'string', title: 'Verification Code', required: true, 'x-decorator': 'FormItem', 'x-component': 'VerifyCode', 'x-component-props': { prefix: "{{icon('LockOutlined')}}", }, 'x-reactions': [ { dependencies: ['.phone#value', '.phone#valid'], fulfill: { state: { 'component[1].readyPost': '{{$deps[0] && $deps[1]}}', 'component[1].phoneNumber': '{{$deps[0]}}', }, }, }, ], }, }, } export default () => { return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card style={{ width: 400 }}> <Tabs style={{ overflow: 'visible', marginTop: -10 }}> <Tabs.TabPane key="1" tab="Password Login"> <Form form={normalForm} layout="vertical" size="large" onAutoSubmit={console.log} > <SchemaField schema={normalSchema} /> <Submit block size="large"> Log in </Submit> </Form> </Tabs.TabPane> <Tabs.TabPane key="2" tab="Mobile Login"> <Form form={phoneForm} layout="vertical" size="large" onAutoSubmit={console.log} > <SchemaField schema={phoneSchema} /> <Submit block size="large"> Log in </Submit> </Form> </Tabs.TabPane> </Tabs> <div style={{ display: 'flex', justifyContent: 'space-between', }} > <a href="#Sign up">Sign up</a> <a href="#Forgot password">Forgot password?</a> </div> </Card> </div> ) } ``` #### Pure JSX Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { Field } from '@formily/react' import { Form, FormItem, Input, Password, Submit } from '@formily/antd' import { Tabs, Card } from 'antd' import { UserOutlined, LockOutlined, PhoneOutlined } from '@ant-design/icons' import { VerifyCode } from './VerifyCode' const normalForm = createForm({ validateFirst: true, }) const phoneForm = createForm({ validateFirst: true, }) export default () => { return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card style={{ width: 400 }}> <Tabs style={{ overflow: 'visible', marginTop: -10 }}> <Tabs.TabPane key="1" tab="Password Login"> <Form form={normalForm} layout="vertical" size="large" onAutoSubmit={console.log} > <Field name="username" title="Username" required decorator={[FormItem]} component={[ Input, { prefix: <UserOutlined />, }, ]} /> <Field name="password" title="Password" required decorator={[FormItem]} component={[ Password, { prefix: <LockOutlined />, }, ]} /> <Submit block size="large"> Log in </Submit> </Form> </Tabs.TabPane> <Tabs.TabPane key="2" tab="Mobile Login"> <Form form={phoneForm} layout="vertical" size="large" onAutoSubmit={console.log} > <Field name="phone" title="Phone Number" required validator="phone" decorator={[FormItem]} component={[ Input, { prefix: <PhoneOutlined />, }, ]} /> <Field name="verifyCode" title="Verification Code" required reactions={(field) => { const phone = field.query('.phone') field.setComponentProps({ readyPost: phone.get('valid') && phone.get('value'), phoneNumber: phone.get('value'), }) }} decorator={[FormItem]} component={[ VerifyCode, { prefix: <LockOutlined />, }, ]} /> <Submit block size="large"> Log in </Submit> </Form> </Tabs.TabPane> </Tabs> <div style={{ display: 'flex', justifyContent: 'space-between', }} > <a href="#Sign up">Sign up</a> <a href="#Forgot password">Forgot password?</a> </div> </Card> </div> ) } ``` ## Sign up #### Markup Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Password, Cascader, DatePicker, Submit, Space, FormGrid, Upload, ArrayItems, Editable, FormButtonGroup, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ validateFirst: true, }) const IDUpload = (props) => { return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button icon={<UploadOutlined />}>Upload a copy</Button> </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, Password, IDUpload, Space, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) export default () => { return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="Sign up" style={{ width: 620 }}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField> <SchemaField.String name="username" title="Username" required x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="password" title="Password" required x-decorator="FormItem" x-component="Password" x-component-props={{ checkStrength: true, }} x-reactions={[ { dependencies: ['.confirm_password'], fulfill: { state: { selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? "Confirm that the password does not match" : ""}}', }, }, }, ]} /> <SchemaField.String name="confirm_password" title="Confirm Password" required x-decorator="FormItem" x-component="Password" x-component-props={{ checkStrength: true, }} x-reactions={[ { dependencies: ['.password'], fulfill: { state: { selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? "Confirm that the password does not match" : ""}}', }, }, }, ]} /> <SchemaField.Void title="name" x-decorator="FormItem" x-decorator-props={{ asterisk: true, feedbackLayout: 'none', }} x-component="FormGrid" > <SchemaField.String name="firstName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: 'firstname', }} required /> <SchemaField.String name="lastName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: 'lastname', }} required /> </SchemaField.Void> <SchemaField.String name="email" title="Email" required x-validator="email" x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="gender" title="Gender" x-decorator="FormItem" x-component="Select" enum={[ { label: 'male', value: 1, }, { label: 'female', value: 2, }, { label: 'third gender', value: 3, }, ]} required /> <SchemaField.String name="birthday" title="Birthday" required x-decorator="FormItem" x-component="DatePicker" /> <SchemaField.String name="address" title="Address" required x-decorator="FormItem" x-component="Cascader" x-reactions="{{fetchAddress}}" /> <SchemaField.String name="idCard" title="ID" required x-decorator="FormItem" x-component="IDUpload" /> <SchemaField.Array name="contacts" title="Contacts" required x-decorator="FormItem" x-component="ArrayItems" > <SchemaField.Object x-component="ArrayItems.Item"> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.SortHandle" /> <SchemaField.Void name="popover" title="Contact Information" x-decorator="Editable.Popover" x-component="FormLayout" x-component-props={{ layout: 'vertical', }} x-reactions={[ { dependencies: ['.popover.name'], fulfill: { schema: { title: '{{$deps[0]}}', }, }, }, ]} > <SchemaField.String name="name" required title="Name" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="email" title="Email" x-validator={[{ required: true }, 'email']} x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="phone" required title="Phone Number" x-validator="phone" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> </SchemaField.Void> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.Remove" /> </SchemaField.Object> <SchemaField.Void x-component="ArrayItems.Addition" title="Add Contact" /> </SchemaField.Array> </SchemaField> <FormButtonGroup.FormItem> <Submit block size="large"> Sign up </Submit> </FormButtonGroup.FormItem> </Form> </Card> </div> ) } ``` #### JSON Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Password, Cascader, DatePicker, Submit, Space, FormGrid, Upload, ArrayItems, Editable, FormButtonGroup, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ validateFirst: true, }) const IDUpload = (props) => { return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button icon={<UploadOutlined />}>Upload a copy</Button> </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, Password, IDUpload, Space, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) const schema = { type: 'object', properties: { username: { type: 'string', title: 'Username', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, password: { type: 'string', title: 'Password', required: true, 'x-decorator': 'FormItem', 'x-component': 'Password', 'x-component-props': { checkStrength: true, }, 'x-reactions': [ { dependencies: ['.confirm_password'], fulfill: { state: { selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? "确认password不匹配" : ""}}', }, }, }, ], }, confirm_password: { type: 'string', title: 'Confirm Password', required: true, 'x-decorator': 'FormItem', 'x-component': 'Password', 'x-component-props': { checkStrength: true, }, 'x-reactions': [ { dependencies: ['.password'], fulfill: { state: { selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? "Confirm that the password does not match" : ""}}', }, }, }, ], }, name: { type: 'void', title: 'name', 'x-decorator': 'FormItem', 'x-decorator-props': { asterisk: true, feedbackLayout: 'none', }, 'x-component': 'FormGrid', properties: { firstName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: 'firstname', }, }, lastName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: 'lastname', }, }, }, }, email: { type: 'string', title: 'Email', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': 'email', }, gender: { type: 'string', title: 'Gender', enum: [ { label: 'male', value: 1, }, { label: 'female', value: 2, }, { label: 'third gender', value: 3, }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', }, birthday: { type: 'string', required: true, title: 'Birthday', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', }, address: { type: 'string', required: true, title: 'Address', 'x-decorator': 'FormItem', 'x-component': 'Cascader', 'x-reactions': '{{fetchAddress}}', }, idCard: { type: 'string', required: true, title: 'ID', 'x-decorator': 'FormItem', 'x-component': 'IDUpload', }, contacts: { type: 'array', required: true, title: 'Contacts', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems', items: { type: 'object', 'x-component': 'ArrayItems.Item', properties: { sort: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.SortHandle', }, popover: { type: 'void', title: 'improve contact information', 'x-decorator': 'Editable.Popover', 'x-component': 'FormLayout', 'x-component-props': { layout: 'vertical', }, 'x-reactions': [ { dependencies: ['.popover.name'], fulfill: { schema: { title: '{{$deps[0]}}', }, }, }, ], properties: { name: { type: 'string', title: 'Name', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { style: { width: 300, }, }, }, email: { type: 'string', title: 'Email', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'email'], 'x-component-props': { style: { width: 300, }, }, }, phone: { type: 'string', title: 'Phone Number', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'phone'], 'x-component-props': { style: { width: 300, }, }, }, }, }, remove: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.Remove', }, }, }, properties: { addition: { type: 'void', title: 'Add Contact', 'x-component': 'ArrayItems.Addition', }, }, }, }, } export default () => { return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="Sign up" style={{ width: 620 }}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField schema={schema} /> <FormButtonGroup.FormItem> <Submit block size="large"> Sign up </Submit> </FormButtonGroup.FormItem> </Form> </Card> </div> ) } ``` #### Pure JSX Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { Field, VoidField, ArrayField } from '@formily/react' import { Form, FormItem, Input, Select, Password, Cascader, DatePicker, Submit, FormGrid, Upload, FormButtonGroup, ArrayBase, Editable, FormLayout, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ validateFirst: true, }) const IDUpload = (props) => { return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button icon={<UploadOutlined />}>Upload a copy</Button> </Upload> ) } export default () => { return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="Sign up" style={{ width: 620 }}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <Field name="username" title="Username" required decorator={[FormItem]} component={[Input]} /> <Field name="password" title="Password" required decorator={[FormItem]} component={[ Password, { checkStrength: true, }, ]} reactions={(field) => { const confirm = field.query('.confirm_password') field.selfErrors = confirm.get('value') && field.value && field.value !== confirm.get('value') ? 'Confirm that the password does not match' : '' }} /> <Field name="confirm_password" title="Confirm Password" required decorator={[FormItem]} component={[ Password, { checkStrength: true, }, ]} reactions={(field) => { const password = field.query('.password') field.selfErrors = password.get('value') && field.value && field.value !== password.get('value') ? 'Confirm that the password does not match' : '' }} /> <VoidField name="name" title="Name" decorator={[ FormItem, { asterisk: true, feedbackLayout: 'none', }, ]} component={[FormGrid]} > <Field name="firstName" decorator={[FormItem]} component={[ Input, { placeholder: 'firstname', }, ]} required /> <Field name="lastName" decorator={[FormItem]} component={[ Input, { placeholder: 'lastname', }, ]} required /> </VoidField> <Field name="email" title="Email" required validator="email" decorator={[FormItem]} component={[Input]} /> <Field name="gender" title="Gender" decorator={[FormItem]} component={[Select]} dataSource={[ { label: 'male', value: 1, }, { label: 'female', value: 2, }, { label: 'third gender', value: 3, }, ]} required /> <Field name="birthday" title="Birthday" required decorator={[FormItem]} component={[DatePicker]} /> <Field name="address" title="Address" required decorator={[FormItem]} component={[Cascader]} reactions={(field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }} /> <Field name="idCard" title="ID" required decorator={[FormItem]} component={[IDUpload]} /> <ArrayField name="contacts" title="Contacts" decorator={[FormItem]}> {(field) => ( <ArrayBase> {field.value?.map((item, index) => ( <div key={index} className="array-items-item"> <Field name={`${index}`} title="Contact Informations" component={[Editable.Popover]} reactions={(field) => { field.title = field.query('.[].name').value() || field.title }} > <VoidField name="layout" component={[FormLayout, { layout: 'vertical' }]} > <Field name="name" title="Name" required decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="email" title="Email" required validator="email" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="phone" title="Phone Number" required validator="phone" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> </VoidField> </Field> <FormItem.BaseItem> <ArrayBase.Remove index={index} /> <ArrayBase.MoveDown index={index} /> <ArrayBase.MoveUp index={index} /> </FormItem.BaseItem> </div> ))} <ArrayBase.Addition title="Add Contact" /> </ArrayBase> )} </ArrayField> <FormButtonGroup.FormItem> <Submit block size="large"> Sign up </Submit> </FormButtonGroup.FormItem> </Form> </Card> </div> ) } ``` ## Forgot password #### Markup Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input, Password, Submit, FormButtonGroup, } from '@formily/antd' import { Card } from 'antd' const form = createForm({ validateFirst: true, }) const SchemaField = createSchemaField({ components: { FormItem, Input, Password, }, }) export default () => { return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="change password" style={{ width: 620 }}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField> <SchemaField.String name="username" title="Username" required x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="email" title="Email" required x-validator="email" x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="old_password" title="Old Password" required x-decorator="FormItem" x-component="Password" /> <SchemaField.String name="password" title="New Password" required x-decorator="FormItem" x-component="Password" x-component-props={{ checkStrength: true, }} x-reactions={[ { dependencies: ['.confirm_password'], fulfill: { state: { selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? "Confirm that the password does not match" : ""}}', }, }, }, ]} /> <SchemaField.String name="confirm_password" title="Confirm Password" required x-decorator="FormItem" x-component="Password" x-component-props={{ checkStrength: true, }} x-reactions={[ { dependencies: ['.password'], fulfill: { state: { selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? "Confirm that the password does not match" : ""}}', }, }, }, ]} /> </SchemaField> <FormButtonGroup.FormItem> <Submit block size="large"> Confirm </Submit> </FormButtonGroup.FormItem> </Form> </Card> </div> ) } ``` #### JSON Schema Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, Input, Password, Submit, FormButtonGroup, } from '@formily/antd' import { Card } from 'antd' const form = createForm({ validateFirst: true, }) const SchemaField = createSchemaField({ components: { FormItem, Input, Password, }, }) const schema = { type: 'object', properties: { username: { type: 'string', title: 'Username', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, email: { type: 'string', title: 'Email', required: true, 'x-validator': 'email', 'x-decorator': 'FormItem', 'x-component': 'Input', }, oldPassword: { type: 'string', title: 'Old Password', required: true, 'x-decorator': 'FormItem', 'x-component': 'Password', }, password: { type: 'string', title: 'New Password', required: true, 'x-decorator': 'FormItem', 'x-component': 'Password', 'x-component-props': { checkStrength: true, }, 'x-reactions': [ { dependencies: ['.confirm_password'], fulfill: { state: { selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? "Confirm that the password does not match" : ""}}', }, }, }, ], }, confirm_password: { type: 'string', title: 'Confirm Password', required: true, 'x-decorator': 'FormItem', 'x-component': 'Password', 'x-component-props': { checkStrength: true, }, 'x-reactions': [ { dependencies: ['.password'], fulfill: { state: { selfErrors: '{{$deps[0] && $self.value && $self.value !== $deps[0] ? "Confirm that the password does not match" : ""}}', }, }, }, ], }, }, } export default () => { return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="change password" style={{ width: 620 }}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField schema={schema} /> <FormButtonGroup.FormItem> <Submit block size="large"> Confirm </Submit> </FormButtonGroup.FormItem> </Form> </Card> </div> ) } ``` #### Pure JSX Cases ```tsx import React from 'react' import { createForm } from '@formily/core' import { Field } from '@formily/react' import { Form, FormItem, Input, Password, Submit, FormButtonGroup, } from '@formily/antd' import { Card } from 'antd' const form = createForm({ validateFirst: true, }) export default () => { return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="change password" style={{ width: 620 }}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <Field name="username" title="Username" required decorator={[FormItem]} component={[Input]} /> <Field name="email" title="Email" required validator="email" decorator={[FormItem]} component={[Input]} /> <Field name="old_password" title="Old Password" required decorator={[FormItem]} component={[Password]} /> <Field name="password" title="New Password" required decorator={[FormItem]} component={[ Password, { checkStrength: true, }, ]} reactions={(field) => { const confirm = field.query('.confirm_password') field.selfErrors = confirm.get('value') && field.value && field.value !== confirm.get('value') ? 'Confirm that the password does not match' : '' }} /> <Field name="confirm_password" title="Confirm Password" required decorator={[FormItem]} component={[ Password, { checkStrength: true, }, ]} reactions={(field) => { const confirm = field.query('.password') field.selfErrors = confirm.get('value') && field.value && field.value !== confirm.get('value') ? 'Confirm that the password does not match' : '' }} /> <FormButtonGroup.FormItem> <Submit block size="large"> Confirm change </Submit> </FormButtonGroup.FormItem> </Form> </Card> </div> ) } ``` ``` -------------------------------------------------------------------------------- /docs/guide/scenes/edit-detail.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # 编辑详情 ## 编辑 #### Markup Schema 案例 ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, Submit, FormGrid, Upload, ArrayItems, Editable, FormButtonGroup, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ validateFirst: true, }) const IDUpload = (props) => { return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button icon={<UploadOutlined />}>上传复印件</Button> </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, IDUpload, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: '张三', phone: '13245633378', email: '[email protected]' }, { name: '李四', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="编辑用户" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField> <SchemaField.String name="username" title="用户名" required x-decorator="FormItem" x-component="Input" /> <SchemaField.Void title="姓名" x-decorator="FormItem" x-decorator-props={{ asterisk: true, feedbackLayout: 'none', }} x-component="FormGrid" > <SchemaField.String name="firstName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: '姓', }} required /> <SchemaField.String name="lastName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: '名', }} required /> </SchemaField.Void> <SchemaField.String name="email" title="邮箱" required x-validator="email" x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="gender" title="性别" x-decorator="FormItem" x-component="Select" enum={[ { label: '男', value: 1, }, { label: '女', value: 2, }, { label: '第三性别', value: 3, }, ]} required /> <SchemaField.String name="birthday" title="生日" required x-decorator="FormItem" x-component="DatePicker" /> <SchemaField.String name="address" title="地址" required x-decorator="FormItem" x-component="Cascader" x-reactions="{{fetchAddress}}" /> <SchemaField.String name="idCard" title="身份证复印件" required x-decorator="FormItem" x-component="IDUpload" /> <SchemaField.Array name="contacts" title="联系人信息" required x-decorator="FormItem" x-component="ArrayItems" > <SchemaField.Object x-component="ArrayItems.Item"> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.SortHandle" /> <SchemaField.Void name="popover" title="维护联系人信息" x-decorator="Editable.Popover" x-component="FormLayout" x-component-props={{ layout: 'vertical', }} x-reactions={[ { fulfill: { schema: { title: '{{$self.query(".name").value() }}', }, }, }, ]} > <SchemaField.String name="name" required title="姓名" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="email" title="邮箱" x-validator={[{ required: true }, 'email']} x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="phone" required title="手机号" x-validator="phone" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> </SchemaField.Void> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.Remove" /> </SchemaField.Object> <SchemaField.Void x-component="ArrayItems.Addition" title="新增联系人" /> </SchemaField.Array> </SchemaField> <FormButtonGroup.FormItem> <Submit block size="large"> 提交 </Submit> </FormButtonGroup.FormItem> </Form> </Spin> </Card> </div> ) } ``` #### JSON Schema 案例 ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, Submit, FormGrid, Upload, ArrayItems, Editable, FormButtonGroup, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ validateFirst: true, }) const IDUpload = (props) => { return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button icon={<UploadOutlined />}>上传复印件</Button> </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, IDUpload, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) const schema = { type: 'object', properties: { username: { type: 'string', title: '用户名', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, name: { type: 'void', title: '姓名', 'x-decorator': 'FormItem', 'x-decorator-props': { asterisk: true, feedbackLayout: 'none', }, 'x-component': 'FormGrid', properties: { firstName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: '姓', }, }, lastName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: '名', }, }, }, }, email: { type: 'string', title: '邮箱', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': 'email', }, gender: { type: 'string', title: '性别', enum: [ { label: '男', value: 1, }, { label: '女', value: 2, }, { label: '第三性别', value: 3, }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', }, birthday: { type: 'string', required: true, title: '生日', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', }, address: { type: 'string', required: true, title: '地址', 'x-decorator': 'FormItem', 'x-component': 'Cascader', 'x-reactions': '{{fetchAddress}}', }, idCard: { type: 'string', required: true, title: '身份证复印件', 'x-decorator': 'FormItem', 'x-component': 'IDUpload', }, contacts: { type: 'array', required: true, title: '联系人信息', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems', items: { type: 'object', 'x-component': 'ArrayItems.Item', properties: { sort: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.SortHandle', }, popover: { type: 'void', title: '完善联系人信息', 'x-decorator': 'Editable.Popover', 'x-component': 'FormLayout', 'x-component-props': { layout: 'vertical', }, 'x-reactions': [ { fulfill: { schema: { title: '{{$self.query(".name").value() }}', }, }, }, ], properties: { name: { type: 'string', title: '姓名', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { style: { width: 300, }, }, }, email: { type: 'string', title: '邮箱', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'email'], 'x-component-props': { style: { width: 300, }, }, }, phone: { type: 'string', title: '手机号', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'phone'], 'x-component-props': { style: { width: 300, }, }, }, }, }, remove: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.Remove', }, }, }, properties: { addition: { type: 'void', title: '新增联系人', 'x-component': 'ArrayItems.Addition', }, }, }, }, } export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: '张三', phone: '13245633378', email: '[email protected]' }, { name: '李四', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="编辑用户" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField schema={schema} /> <FormButtonGroup.FormItem> <Submit block size="large"> 提交 </Submit> </FormButtonGroup.FormItem> </Form> </Spin> </Card> </div> ) } ``` #### 纯 JSX 案例 ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { Field, VoidField, ArrayField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, Submit, FormGrid, Upload, ArrayBase, Editable, FormButtonGroup, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' import './index.less' const form = createForm({ validateFirst: true, }) const IDUpload = (props) => { return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button icon={<UploadOutlined />}>上传复印件</Button> </Upload> ) } const fetchAddress = (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) } export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: '张三', phone: '13245633378', email: '[email protected]' }, { name: '李四', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <Card title="编辑用户" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <Field name="username" title="用户名" required decorator={[FormItem]} component={[Input]} /> <VoidField name="name" title="姓名" decorator={[ FormItem, { asterisk: true, feedbackLayout: 'none', }, ]} component={[FormGrid]} > <Field name="firstName" decorator={[FormItem]} component={[ Input, { placeholder: '姓', }, ]} required /> <Field name="lastName" decorator={[FormItem]} component={[ Input, { placeholder: '名', }, ]} required /> </VoidField> <Field name="email" title="邮箱" required validator="email" decorator={[FormItem]} component={[Input]} /> <Field name="gender" title="性别" decorator={[FormItem]} component={[Select]} dataSource={[ { label: '男', value: 1, }, { label: '女', value: 2, }, { label: '第三性别', value: 3, }, ]} required /> <Field name="birthday" title="生日" required decorator={[FormItem]} component={[DatePicker]} /> <Field name="address" title="地址" required decorator={[FormItem]} component={[Cascader]} reactions={fetchAddress} /> <Field name="idCard" title="身份证复印件" required decorator={[FormItem]} component={[IDUpload]} /> <ArrayField name="contacts" title="联系人信息" decorator={[FormItem]} > {(field) => ( <ArrayBase> {field.value?.map((item, index) => ( <div key={index} className="array-items-item"> <Field name={`${index}`} title="完善联系人信息" component={[Editable.Popover]} reactions={(field) => { field.title = field.query('.[].name').value() || field.title }} > <VoidField name="layout" component={[FormLayout, { layout: 'vertical' }]} > <Field name="name" title="姓名" required decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="email" title="邮箱" required validator="email" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="phone" title="手机号" required validator="phone" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> </VoidField> </Field> <FormItem.BaseItem> <ArrayBase.Remove index={index} /> <ArrayBase.MoveDown index={index} /> <ArrayBase.MoveUp index={index} /> </FormItem.BaseItem> </div> ))} <ArrayBase.Addition title="新增联系人" /> </ArrayBase> )} </ArrayField> <FormButtonGroup.FormItem> <Submit block size="large"> 提交 </Submit> </FormButtonGroup.FormItem> </Form> </Spin> </Card> </div> ) } ``` ## 详情 #### Markup Schema 案例 ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { createSchemaField, useField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, FormGrid, Upload, ArrayItems, Editable, PreviewText, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ readPretty: true, validateFirst: true, }) const IDUpload = (props) => { const field = useField() return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > {field.editable && <Button icon={<UploadOutlined />}>上传复印件</Button>} </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, IDUpload, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: '张三', phone: '13245633378', email: '[email protected]' }, { name: '李四', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <PreviewText.Placeholder value="-"> <Card title="用户详情" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField> <SchemaField.String name="username" title="用户名" required x-decorator="FormItem" x-component="Input" /> <SchemaField.Void title="姓名" x-decorator="FormItem" x-decorator-props={{ feedbackLayout: 'none', }} x-component="FormGrid" > <SchemaField.String name="firstName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: '姓', }} required /> <SchemaField.String name="lastName" x-decorator="FormItem" x-component="Input" x-component-props={{ placeholder: '名', }} required /> </SchemaField.Void> <SchemaField.String name="email" title="邮箱" required x-validator="email" x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="gender" title="性别" x-decorator="FormItem" x-component="Select" enum={[ { label: '男', value: 1, }, { label: '女', value: 2, }, { label: '第三性别', value: 3, }, ]} required /> <SchemaField.String name="birthday" title="生日" required x-decorator="FormItem" x-component="DatePicker" /> <SchemaField.String name="address" title="地址" required x-decorator="FormItem" x-component="Cascader" x-reactions="{{fetchAddress}}" /> <SchemaField.String name="idCard" title="身份证复印件" required x-decorator="FormItem" x-component="IDUpload" /> <SchemaField.Array name="contacts" title="联系人信息" required x-decorator="FormItem" x-component="ArrayItems" > <SchemaField.Object x-component="ArrayItems.Item"> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.SortHandle" /> <SchemaField.Void name="popover" title="维护联系人信息" x-decorator="Editable.Popover" x-component="FormLayout" x-component-props={{ layout: 'vertical', }} x-reactions={[ { fulfill: { schema: { title: '{{$self.query(".name").value() }}', }, }, }, ]} > <SchemaField.String name="name" required title="姓名" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="email" title="邮箱" x-validator={[{ required: true }, 'email']} x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> <SchemaField.String name="phone" required title="手机号" x-validator="phone" x-decorator="FormItem" x-component="Input" x-component-props={{ style: { width: 300, }, }} /> </SchemaField.Void> <SchemaField.Void x-decorator="FormItem" x-component="ArrayItems.Remove" /> </SchemaField.Object> <SchemaField.Void x-component="ArrayItems.Addition" title="新增联系人" /> </SchemaField.Array> </SchemaField> </Form> </Spin> </Card> </PreviewText.Placeholder> </div> ) } ``` #### JSON Schema 案例 ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { createSchemaField, useField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, FormGrid, Upload, ArrayItems, Editable, PreviewText, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' const form = createForm({ readPretty: true, validateFirst: true, }) const IDUpload = (props) => { const field = useField() return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > {field.editable && <Button icon={<UploadOutlined />}>上传复印件</Button>} </Upload> ) } const SchemaField = createSchemaField({ components: { FormItem, FormGrid, FormLayout, Input, DatePicker, Cascader, Select, IDUpload, ArrayItems, Editable, }, scope: { fetchAddress: (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) }, }, }) const schema = { type: 'object', properties: { username: { type: 'string', title: '用户名', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, name: { type: 'void', title: '姓名', 'x-decorator': 'FormItem', 'x-decorator-props': { asterisk: true, feedbackLayout: 'none', }, 'x-component': 'FormGrid', properties: { firstName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: '姓', }, }, lastName: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { placeholder: '名', }, }, }, }, email: { type: 'string', title: '邮箱', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': 'email', }, gender: { type: 'string', title: '性别', enum: [ { label: '男', value: 1, }, { label: '女', value: 2, }, { label: '第三性别', value: 3, }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', }, birthday: { type: 'string', required: true, title: '生日', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', }, address: { type: 'string', required: true, title: '地址', 'x-decorator': 'FormItem', 'x-component': 'Cascader', 'x-reactions': '{{fetchAddress}}', }, idCard: { type: 'string', required: true, title: '身份证复印件', 'x-decorator': 'FormItem', 'x-component': 'IDUpload', }, contacts: { type: 'array', required: true, title: '联系人信息', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems', items: { type: 'object', 'x-component': 'ArrayItems.Item', properties: { sort: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.SortHandle', }, popover: { type: 'void', title: '完善联系人信息', 'x-decorator': 'Editable.Popover', 'x-component': 'FormLayout', 'x-component-props': { layout: 'vertical', }, 'x-reactions': [ { fulfill: { schema: { title: '{{$self.query(".name").value() }}', }, }, }, ], properties: { name: { type: 'string', title: '姓名', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { style: { width: 300, }, }, }, email: { type: 'string', title: '邮箱', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'email'], 'x-component-props': { style: { width: 300, }, }, }, phone: { type: 'string', title: '手机号', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true }, 'phone'], 'x-component-props': { style: { width: 300, }, }, }, }, }, remove: { type: 'void', 'x-decorator': 'FormItem', 'x-component': 'ArrayItems.Remove', }, }, }, properties: { addition: { type: 'void', title: '新增联系人', 'x-component': 'ArrayItems.Addition', }, }, }, }, } export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: '张三', phone: '13245633378', email: '[email protected]' }, { name: '李四', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <PreviewText.Placeholder value="-"> <Card title="用户详情" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <SchemaField schema={schema} /> </Form> </Spin> </Card> </PreviewText.Placeholder> </div> ) } ``` #### 纯 JSX 案例 ```tsx import React, { useState, useEffect } from 'react' import { createForm } from '@formily/core' import { Field, VoidField, ArrayField, useField } from '@formily/react' import { Form, FormItem, FormLayout, Input, Select, Cascader, DatePicker, FormGrid, ArrayBase, Upload, PreviewText, Editable, } from '@formily/antd' import { action } from '@formily/reactive' import { Card, Button, Spin } from 'antd' import { UploadOutlined } from '@ant-design/icons' import './index.less' const form = createForm({ validateFirst: true, readPretty: true, }) const IDUpload = (props) => { const field = useField() return ( <Upload {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > {field.editable && <Button icon={<UploadOutlined />}>上传复印件</Button>} </Upload> ) } const fetchAddress = (field) => { const transform = (data = {}) => { return Object.entries(data).reduce((buf, [key, value]) => { if (typeof value === 'string') return buf.concat({ label: value, value: key, }) const { name, code, cities, districts } = value const _cities = transform(cities) const _districts = transform(districts) return buf.concat({ label: name, value: code, children: _cities.length ? _cities : _districts.length ? _districts : undefined, }) }, []) } field.loading = true fetch('//unpkg.com/china-location/dist/location.json') .then((res) => res.json()) .then( action.bound((data) => { field.dataSource = transform(data) field.loading = false }) ) } export default () => { const [loading, setLoading] = useState(true) useEffect(() => { setTimeout(() => { form.setInitialValues({ username: 'Aston Martin', firstName: 'Aston', lastName: 'Martin', email: '[email protected]', gender: 1, birthday: '1836-01-03', address: ['110000', '110000', '110101'], idCard: [ { name: 'this is image', thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', uid: 'rc-upload-1615825692847-2', url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', }, ], contacts: [ { name: '张三', phone: '13245633378', email: '[email protected]' }, { name: '李四', phone: '16873452678', email: '[email protected]' }, ], }) setLoading(false) }, 2000) }, []) return ( <div style={{ display: 'flex', justifyContent: 'center', background: '#eee', padding: '40px 0', }} > <PreviewText.Placeholder value="-"> <Card title="编辑用户" style={{ width: 620 }}> <Spin spinning={loading}> <Form form={form} labelCol={5} wrapperCol={16} onAutoSubmit={console.log} > <Field name="username" title="用户名" required decorator={[FormItem]} component={[Input]} /> <VoidField name="name" title="姓名" decorator={[ FormItem, { feedbackLayout: 'none', }, ]} component={[FormGrid]} > <Field name="firstName" decorator={[FormItem]} component={[ Input, { placeholder: '姓', }, ]} required /> <Field name="lastName" decorator={[FormItem]} component={[ Input, { placeholder: '名', }, ]} required /> </VoidField> <Field name="email" title="邮箱" required validator="email" decorator={[FormItem]} component={[Input]} /> <Field name="gender" title="性别" decorator={[FormItem]} component={[Select]} dataSource={[ { label: '男', value: 1, }, { label: '女', value: 2, }, { label: '第三性别', value: 3, }, ]} required /> <Field name="birthday" title="生日" required decorator={[FormItem]} component={[DatePicker]} /> <Field name="address" title="地址" required decorator={[FormItem]} component={[Cascader]} reactions={fetchAddress} /> <Field name="idCard" title="身份证复印件" required decorator={[FormItem]} component={[IDUpload]} /> <ArrayField name="contacts" title="联系人信息" decorator={[FormItem]} > {(field) => ( <ArrayBase> {field.value?.map((item, index) => ( <div key={index} className="array-items-item"> <Field name={`${index}`} title="完善联系人信息" component={[Editable.Popover]} reactions={(field) => { field.title = field.query('.[].name').value() || field.title }} > <VoidField name="layout" component={[FormLayout, { layout: 'vertical' }]} > <Field name="name" title="姓名" required decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="email" title="邮箱" required validator="email" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> <Field name="phone" title="手机号" required validator="phone" decorator={[FormItem]} component={[ Input, { style: { width: 300, }, }, ]} /> </VoidField> </Field> </div> ))} <ArrayBase.Addition title="新增联系人" /> </ArrayBase> )} </ArrayField> </Form> </Spin> </Card> </PreviewText.Placeholder> </div> ) } ``` ```