This is page 48 of 52. Use http://codebase.md/alibaba/formily?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .all-contributorsrc ├── .codecov.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows │ ├── check-pr-title.yml │ ├── ci.yml │ ├── commitlint.yml │ ├── issue-open-check.yml │ ├── package-size.yml │ └── pr-welcome.yml ├── .gitignore ├── .prettierrc.js ├── .umirc.js ├── .vscode │ └── cspell.json ├── .yarnrc ├── CHANGELOG.md ├── commitlint.config.js ├── devtools │ ├── .eslintrc │ └── chrome-extension │ ├── .npmignore │ ├── assets │ │ └── img │ │ ├── loading.svg │ │ └── logo │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 38x38.png │ │ ├── 48x48.png │ │ ├── error.png │ │ ├── gray.png │ │ └── scalable.png │ ├── config │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── components │ │ │ │ ├── FieldTree.tsx │ │ │ │ ├── filter.ts │ │ │ │ ├── LeftPanel.tsx │ │ │ │ ├── RightPanel.tsx │ │ │ │ ├── SearchBox.tsx │ │ │ │ └── Tabs.tsx │ │ │ ├── demo.tsx │ │ │ └── index.tsx │ │ └── extension │ │ ├── backend.ts │ │ ├── background.ts │ │ ├── content.ts │ │ ├── devpanel.tsx │ │ ├── devtools.tsx │ │ ├── inject.ts │ │ ├── manifest.json │ │ ├── popup.tsx │ │ └── views │ │ ├── devpanel.ejs │ │ ├── devtools.ejs │ │ └── popup.ejs │ ├── tsconfig.build.json │ └── tsconfig.json ├── docs │ ├── functions │ │ ├── contributors.ts │ │ └── npm-search.ts │ ├── guide │ │ ├── advanced │ │ │ ├── async.md │ │ │ ├── async.zh-CN.md │ │ │ ├── build.md │ │ │ ├── build.zh-CN.md │ │ │ ├── business-logic.md │ │ │ ├── business-logic.zh-CN.md │ │ │ ├── calculator.md │ │ │ ├── calculator.zh-CN.md │ │ │ ├── controlled.md │ │ │ ├── controlled.zh-CN.md │ │ │ ├── custom.md │ │ │ ├── custom.zh-CN.md │ │ │ ├── destructor.md │ │ │ ├── destructor.zh-CN.md │ │ │ ├── input.less │ │ │ ├── layout.md │ │ │ ├── layout.zh-CN.md │ │ │ ├── linkages.md │ │ │ ├── linkages.zh-CN.md │ │ │ ├── validate.md │ │ │ └── validate.zh-CN.md │ │ ├── contribution.md │ │ ├── contribution.zh-CN.md │ │ ├── form-builder.md │ │ ├── form-builder.zh-CN.md │ │ ├── index.md │ │ ├── index.zh-CN.md │ │ ├── issue-helper.md │ │ ├── issue-helper.zh-CN.md │ │ ├── learn-formily.md │ │ ├── learn-formily.zh-CN.md │ │ ├── quick-start.md │ │ ├── quick-start.zh-CN.md │ │ ├── scenes │ │ │ ├── dialog-drawer.md │ │ │ ├── dialog-drawer.zh-CN.md │ │ │ ├── edit-detail.md │ │ │ ├── edit-detail.zh-CN.md │ │ │ ├── index.less │ │ │ ├── login-register.md │ │ │ ├── login-register.zh-CN.md │ │ │ ├── more.md │ │ │ ├── more.zh-CN.md │ │ │ ├── query-list.md │ │ │ ├── query-list.zh-CN.md │ │ │ ├── step-form.md │ │ │ ├── step-form.zh-CN.md │ │ │ ├── tab-form.md │ │ │ ├── tab-form.zh-CN.md │ │ │ └── VerifyCode.tsx │ │ ├── upgrade.md │ │ └── upgrade.zh-CN.md │ ├── index.md │ ├── index.zh-CN.md │ └── site │ ├── Contributors.less │ ├── Contributors.tsx │ ├── QrCode.less │ ├── QrCode.tsx │ ├── Section.less │ ├── Section.tsx │ └── styles.less ├── global.config.ts ├── jest.config.js ├── lerna.json ├── LICENSE.md ├── package.json ├── packages │ ├── .eslintrc │ ├── antd │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── ArrayTabs.md │ │ │ │ ├── ArrayTabs.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── sort.tsx │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.less │ │ │ │ ├── grid.less │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── style.less │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── benchmark │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ └── index.tsx │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── core │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── entry │ │ │ │ │ ├── ActionResponse.less │ │ │ │ │ ├── ActionResponse.tsx │ │ │ │ │ ├── createForm.md │ │ │ │ │ ├── createForm.zh-CN.md │ │ │ │ │ ├── FieldEffectHooks.md │ │ │ │ │ ├── FieldEffectHooks.zh-CN.md │ │ │ │ │ ├── FormChecker.md │ │ │ │ │ ├── FormChecker.zh-CN.md │ │ │ │ │ ├── FormEffectHooks.md │ │ │ │ │ ├── FormEffectHooks.zh-CN.md │ │ │ │ │ ├── FormHooksAPI.md │ │ │ │ │ ├── FormHooksAPI.zh-CN.md │ │ │ │ │ ├── FormPath.md │ │ │ │ │ ├── FormPath.zh-CN.md │ │ │ │ │ ├── FormValidatorRegistry.md │ │ │ │ │ └── FormValidatorRegistry.zh-CN.md │ │ │ │ └── models │ │ │ │ ├── ArrayField.md │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ ├── Field.md │ │ │ │ ├── Field.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── ObjectField.md │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ ├── Query.md │ │ │ │ ├── Query.zh-CN.md │ │ │ │ ├── VoidField.md │ │ │ │ └── VoidField.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── field.md │ │ │ │ ├── field.zh-CN.md │ │ │ │ ├── form.md │ │ │ │ ├── form.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── mvvm.md │ │ │ │ ├── mvvm.zh-CN.md │ │ │ │ ├── values.md │ │ │ │ └── values.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── array.spec.ts │ │ │ │ ├── effects.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── field.spec.ts │ │ │ │ ├── form.spec.ts │ │ │ │ ├── graph.spec.ts │ │ │ │ ├── heart.spec.ts │ │ │ │ ├── internals.spec.ts │ │ │ │ ├── lifecycle.spec.ts │ │ │ │ ├── object.spec.ts │ │ │ │ ├── shared.ts │ │ │ │ └── void.spec.ts │ │ │ ├── effects │ │ │ │ ├── index.ts │ │ │ │ ├── onFieldEffects.ts │ │ │ │ └── onFormEffects.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── ArrayField.ts │ │ │ │ ├── BaseField.ts │ │ │ │ ├── Field.ts │ │ │ │ ├── Form.ts │ │ │ │ ├── Graph.ts │ │ │ │ ├── Heart.ts │ │ │ │ ├── index.ts │ │ │ │ ├── LifeCycle.ts │ │ │ │ ├── ObjectField.ts │ │ │ │ ├── Query.ts │ │ │ │ ├── types.ts │ │ │ │ └── VoidField.ts │ │ │ ├── shared │ │ │ │ ├── checkers.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── effective.ts │ │ │ │ ├── externals.ts │ │ │ │ └── internals.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── element │ │ ├── .npmignore │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── .vuepress │ │ │ │ ├── components │ │ │ │ │ ├── createCodeSandBox.js │ │ │ │ │ ├── dumi-previewer.vue │ │ │ │ │ └── highlight.js │ │ │ │ ├── config.js │ │ │ │ ├── enhanceApp.js │ │ │ │ ├── styles │ │ │ │ │ └── index.styl │ │ │ │ └── util.js │ │ │ ├── demos │ │ │ │ ├── guide │ │ │ │ │ ├── array-cards │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-collapse │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-items │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-table │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-tabs │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── cascader │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── checkbox │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── date-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── editable │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-button-group.vue │ │ │ │ │ ├── form-collapse │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-dialog │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-drawer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-grid │ │ │ │ │ │ ├── form.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── native.vue │ │ │ │ │ ├── form-item │ │ │ │ │ │ ├── bordered-none.vue │ │ │ │ │ │ ├── common.vue │ │ │ │ │ │ ├── feedback.vue │ │ │ │ │ │ ├── inset.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ ├── size.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-layout │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-step │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-tab │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form.vue │ │ │ │ │ ├── input │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── input-number │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── password │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── preview-text │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── extend.vue │ │ │ │ │ ├── radio │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── reset │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ ├── force.vue │ │ │ │ │ │ └── validate.vue │ │ │ │ │ ├── select │ │ │ │ │ │ ├── json-schema-async.vue │ │ │ │ │ │ ├── json-schema-sync.vue │ │ │ │ │ │ ├── markup-schema-async-search.vue │ │ │ │ │ │ ├── markup-schema-async.vue │ │ │ │ │ │ ├── markup-schema-sync.vue │ │ │ │ │ │ ├── template-async.vue │ │ │ │ │ │ └── template-sync.vue │ │ │ │ │ ├── space │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── submit │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── loading.vue │ │ │ │ │ ├── switch │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── time-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── transfer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ └── upload │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ └── template.vue │ │ │ │ └── index.vue │ │ │ ├── guide │ │ │ │ ├── array-cards.md │ │ │ │ ├── array-collapse.md │ │ │ │ ├── array-items.md │ │ │ │ ├── array-table.md │ │ │ │ ├── array-tabs.md │ │ │ │ ├── cascader.md │ │ │ │ ├── checkbox.md │ │ │ │ ├── date-picker.md │ │ │ │ ├── editable.md │ │ │ │ ├── form-button-group.md │ │ │ │ ├── form-collapse.md │ │ │ │ ├── form-dialog.md │ │ │ │ ├── form-drawer.md │ │ │ │ ├── form-grid.md │ │ │ │ ├── form-item.md │ │ │ │ ├── form-layout.md │ │ │ │ ├── form-step.md │ │ │ │ ├── form-tab.md │ │ │ │ ├── form.md │ │ │ │ ├── index.md │ │ │ │ ├── input-number.md │ │ │ │ ├── input.md │ │ │ │ ├── password.md │ │ │ │ ├── preview-text.md │ │ │ │ ├── radio.md │ │ │ │ ├── reset.md │ │ │ │ ├── select.md │ │ │ │ ├── space.md │ │ │ │ ├── submit.md │ │ │ │ ├── switch.md │ │ │ │ ├── time-picker.md │ │ │ │ ├── transfer.md │ │ │ │ └── upload.md │ │ │ └── README.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── configs │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared │ │ │ │ │ ├── create-context.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loading.ts │ │ │ │ │ ├── portal.ts │ │ │ │ │ ├── resolve-component.ts │ │ │ │ │ ├── transform-component.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── styles │ │ │ │ └── common.scss │ │ │ ├── array-base │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── el-form │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── el-form-item │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── var.scss │ │ │ ├── form-layout │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── input-number │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── space │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.ts │ │ │ └── style.ts │ │ ├── transformer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── grid │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── index.ts │ │ │ └── observer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── json-schema │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── schema.spec.ts.snap │ │ │ │ ├── compiler.spec.ts │ │ │ │ ├── patches.spec.ts │ │ │ │ ├── schema.spec.ts │ │ │ │ ├── server-validate.spec.ts │ │ │ │ ├── shared.spec.ts │ │ │ │ ├── transformer.spec.ts │ │ │ │ └── traverse.spec.ts │ │ │ ├── compiler.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── patches.ts │ │ │ ├── polyfills │ │ │ │ ├── index.ts │ │ │ │ └── SPECIFICATION_1_0.ts │ │ │ ├── schema.ts │ │ │ ├── shared.ts │ │ │ ├── transformer.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── next │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── DatePicker2.md │ │ │ │ ├── DatePicker2.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── TimePicker2.md │ │ │ │ ├── TimePicker2.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LESENCE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── empty.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── mapSize.ts │ │ │ │ ├── mapStatus.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── toArray.ts │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker2 │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── scss │ │ │ │ │ └── variable.scss │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── main.scss │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker2 │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── main.scss │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── path │ │ ├── .npmignore │ │ ├── benchmark.ts │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── accessor.spec.ts │ │ │ │ ├── basic.spec.ts │ │ │ │ ├── match.spec.ts │ │ │ │ ├── parser.spec.ts │ │ │ │ └── share.spec.ts │ │ │ ├── contexts.ts │ │ │ ├── destructor.ts │ │ │ ├── index.ts │ │ │ ├── matcher.ts │ │ │ ├── parser.ts │ │ │ ├── shared.ts │ │ │ ├── tokenizer.ts │ │ │ ├── tokens.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── ArrayField.md │ │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ │ ├── ExpressionScope.md │ │ │ │ │ ├── ExpressionScope.zh-CN.md │ │ │ │ │ ├── Field.md │ │ │ │ │ ├── Field.zh-CN.md │ │ │ │ │ ├── FormConsumer.md │ │ │ │ │ ├── FormConsumer.zh-CN.md │ │ │ │ │ ├── FormProvider.md │ │ │ │ │ ├── FormProvider.zh-CN.md │ │ │ │ │ ├── ObjectField.md │ │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ │ ├── RecordScope.md │ │ │ │ │ ├── RecordScope.zh-CN.md │ │ │ │ │ ├── RecordsScope.md │ │ │ │ │ ├── RecordsScope.zh-CN.md │ │ │ │ │ ├── RecursionField.md │ │ │ │ │ ├── RecursionField.zh-CN.md │ │ │ │ │ ├── SchemaField.md │ │ │ │ │ ├── SchemaField.zh-CN.md │ │ │ │ │ ├── VoidField.md │ │ │ │ │ └── VoidField.zh-CN.md │ │ │ │ ├── hooks │ │ │ │ │ ├── useExpressionScope.md │ │ │ │ │ ├── useExpressionScope.zh-CN.md │ │ │ │ │ ├── useField.md │ │ │ │ │ ├── useField.zh-CN.md │ │ │ │ │ ├── useFieldSchema.md │ │ │ │ │ ├── useFieldSchema.zh-CN.md │ │ │ │ │ ├── useForm.md │ │ │ │ │ ├── useForm.zh-CN.md │ │ │ │ │ ├── useFormEffects.md │ │ │ │ │ ├── useFormEffects.zh-CN.md │ │ │ │ │ ├── useParentForm.md │ │ │ │ │ └── useParentForm.zh-CN.md │ │ │ │ └── shared │ │ │ │ ├── connect.md │ │ │ │ ├── connect.zh-CN.md │ │ │ │ ├── context.md │ │ │ │ ├── context.zh-CN.md │ │ │ │ ├── mapProps.md │ │ │ │ ├── mapProps.zh-CN.md │ │ │ │ ├── mapReadPretty.md │ │ │ │ ├── mapReadPretty.zh-CN.md │ │ │ │ ├── observer.md │ │ │ │ ├── observer.zh-CN.md │ │ │ │ ├── Schema.md │ │ │ │ └── Schema.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── expression.spec.tsx │ │ │ │ ├── field.spec.tsx │ │ │ │ ├── form.spec.tsx │ │ │ │ ├── schema.json.spec.tsx │ │ │ │ ├── schema.markup.spec.tsx │ │ │ │ └── shared.tsx │ │ │ ├── components │ │ │ │ ├── ArrayField.tsx │ │ │ │ ├── ExpressionScope.tsx │ │ │ │ ├── Field.tsx │ │ │ │ ├── FormConsumer.tsx │ │ │ │ ├── FormProvider.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── ObjectField.tsx │ │ │ │ ├── ReactiveField.tsx │ │ │ │ ├── RecordScope.tsx │ │ │ │ ├── RecordsScope.tsx │ │ │ │ ├── RecursionField.tsx │ │ │ │ ├── SchemaField.tsx │ │ │ │ └── VoidField.tsx │ │ │ ├── global.d.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useAttach.ts │ │ │ │ ├── useExpressionScope.ts │ │ │ │ ├── useField.ts │ │ │ │ ├── useFieldSchema.ts │ │ │ │ ├── useForm.ts │ │ │ │ ├── useFormEffects.ts │ │ │ │ └── useParentForm.ts │ │ │ ├── index.ts │ │ │ ├── shared │ │ │ │ ├── connect.ts │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ └── render.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── benchmark.ts │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── action.md │ │ │ │ ├── action.zh-CN.md │ │ │ │ ├── autorun.md │ │ │ │ ├── autorun.zh-CN.md │ │ │ │ ├── batch.md │ │ │ │ ├── batch.zh-CN.md │ │ │ │ ├── define.md │ │ │ │ ├── define.zh-CN.md │ │ │ │ ├── hasCollected.md │ │ │ │ ├── hasCollected.zh-CN.md │ │ │ │ ├── markObservable.md │ │ │ │ ├── markObservable.zh-CN.md │ │ │ │ ├── markRaw.md │ │ │ │ ├── markRaw.zh-CN.md │ │ │ │ ├── model.md │ │ │ │ ├── model.zh-CN.md │ │ │ │ ├── observable.md │ │ │ │ ├── observable.zh-CN.md │ │ │ │ ├── observe.md │ │ │ │ ├── observe.zh-CN.md │ │ │ │ ├── raw.md │ │ │ │ ├── raw.zh-CN.md │ │ │ │ ├── react │ │ │ │ │ ├── observer.md │ │ │ │ │ └── observer.zh-CN.md │ │ │ │ ├── reaction.md │ │ │ │ ├── reaction.zh-CN.md │ │ │ │ ├── toJS.md │ │ │ │ ├── toJS.zh-CN.md │ │ │ │ ├── tracker.md │ │ │ │ ├── tracker.zh-CN.md │ │ │ │ ├── typeChecker.md │ │ │ │ ├── typeChecker.zh-CN.md │ │ │ │ ├── untracked.md │ │ │ │ ├── untracked.zh-CN.md │ │ │ │ └── vue │ │ │ │ ├── observer.md │ │ │ │ └── observer.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── best-practice.md │ │ │ │ ├── best-practice.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── action.spec.ts │ │ │ │ ├── annotations.spec.ts │ │ │ │ ├── array.spec.ts │ │ │ │ ├── autorun.spec.ts │ │ │ │ ├── batch.spec.ts │ │ │ │ ├── collections-map.spec.ts │ │ │ │ ├── collections-set.spec.ts │ │ │ │ ├── collections-weakmap.spec.ts │ │ │ │ ├── collections-weakset.spec.ts │ │ │ │ ├── define.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── hasCollected.spec.ts │ │ │ │ ├── observable.spec.ts │ │ │ │ ├── observe.spec.ts │ │ │ │ ├── tracker.spec.ts │ │ │ │ └── untracked.spec.ts │ │ │ ├── action.ts │ │ │ ├── annotations │ │ │ │ ├── box.ts │ │ │ │ ├── computed.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observable.ts │ │ │ │ ├── ref.ts │ │ │ │ └── shallow.ts │ │ │ ├── array.ts │ │ │ ├── autorun.ts │ │ │ ├── batch.ts │ │ │ ├── checkers.ts │ │ │ ├── environment.ts │ │ │ ├── externals.ts │ │ │ ├── global.d.ts │ │ │ ├── handlers.ts │ │ │ ├── index.ts │ │ │ ├── internals.ts │ │ │ ├── model.ts │ │ │ ├── observable.ts │ │ │ ├── observe.ts │ │ │ ├── reaction.ts │ │ │ ├── tracker.ts │ │ │ ├── tree.ts │ │ │ ├── types.ts │ │ │ └── untracked.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useCompatEffect.ts │ │ │ │ ├── useCompatFactory.ts │ │ │ │ ├── useDidUpdate.ts │ │ │ │ ├── useForceUpdate.ts │ │ │ │ ├── useLayoutEffect.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer.ts │ │ │ ├── shared │ │ │ │ ├── gc.ts │ │ │ │ ├── global.ts │ │ │ │ ├── immediate.ts │ │ │ │ └── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-test-cases-for-react18 │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.js │ │ │ └── MySlowList.js │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── reactive-vue │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── observer.spec.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer │ │ │ │ ├── collectData.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observerInVue2.ts │ │ │ │ └── observerInVue3.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── shared │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ ├── array.ts │ │ │ ├── case.ts │ │ │ ├── checkers.ts │ │ │ ├── clone.ts │ │ │ ├── compare.ts │ │ │ ├── defaults.ts │ │ │ ├── deprecate.ts │ │ │ ├── global.ts │ │ │ ├── index.ts │ │ │ ├── instanceof.ts │ │ │ ├── isEmpty.ts │ │ │ ├── merge.ts │ │ │ ├── middleware.ts │ │ │ ├── path.ts │ │ │ ├── string.ts │ │ │ ├── subscribable.ts │ │ │ └── uid.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── validator │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── parser.spec.ts │ │ │ │ ├── registry.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── formats.ts │ │ │ ├── index.ts │ │ │ ├── locale.ts │ │ │ ├── parser.ts │ │ │ ├── registry.ts │ │ │ ├── rules.ts │ │ │ ├── template.ts │ │ │ ├── types.ts │ │ │ └── validator.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── vue │ ├── .npmignore │ ├── bin │ │ ├── formily-vue-fix.js │ │ └── formily-vue-switch.js │ ├── docs │ │ ├── .vuepress │ │ │ ├── components │ │ │ │ ├── createCodeSandBox.js │ │ │ │ ├── dumi-previewer.vue │ │ │ │ └── highlight.js │ │ │ ├── config.js │ │ │ ├── enhanceApp.js │ │ │ └── styles │ │ │ └── index.styl │ │ ├── api │ │ │ ├── components │ │ │ │ ├── array-field.md │ │ │ │ ├── expression-scope.md │ │ │ │ ├── field.md │ │ │ │ ├── form-consumer.md │ │ │ │ ├── form-provider.md │ │ │ │ ├── object-field.md │ │ │ │ ├── recursion-field-with-component.md │ │ │ │ ├── recursion-field.md │ │ │ │ ├── schema-field-with-schema.md │ │ │ │ ├── schema-field.md │ │ │ │ └── void-field.md │ │ │ ├── hooks │ │ │ │ ├── use-field-schema.md │ │ │ │ ├── use-field.md │ │ │ │ ├── use-form-effects.md │ │ │ │ ├── use-form.md │ │ │ │ └── use-parent-form.md │ │ │ └── shared │ │ │ ├── connect.md │ │ │ ├── injections.md │ │ │ ├── map-props.md │ │ │ ├── map-read-pretty.md │ │ │ ├── observer.md │ │ │ └── schema.md │ │ ├── demos │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── array-field.vue │ │ │ │ │ ├── expression-scope.vue │ │ │ │ │ ├── field.vue │ │ │ │ │ ├── form-consumer.vue │ │ │ │ │ ├── form-provider.vue │ │ │ │ │ ├── object-field.vue │ │ │ │ │ ├── recursion-field-with-component.vue │ │ │ │ │ ├── recursion-field.vue │ │ │ │ │ ├── schema-field-with-schema.vue │ │ │ │ │ ├── schema-field.vue │ │ │ │ │ └── void-field.vue │ │ │ │ ├── hooks │ │ │ │ │ ├── use-field-schema.vue │ │ │ │ │ ├── use-field.vue │ │ │ │ │ ├── use-form-effects.vue │ │ │ │ │ ├── use-form.vue │ │ │ │ │ └── use-parent-form.vue │ │ │ │ └── shared │ │ │ │ ├── connect.vue │ │ │ │ ├── map-props.vue │ │ │ │ ├── map-read-pretty.vue │ │ │ │ └── observer.vue │ │ │ ├── index.vue │ │ │ └── questions │ │ │ ├── default-slot.vue │ │ │ ├── events.vue │ │ │ ├── named-slot.vue │ │ │ └── scoped-slot.vue │ │ ├── guide │ │ │ ├── architecture.md │ │ │ ├── concept.md │ │ │ └── README.md │ │ ├── questions │ │ │ └── README.md │ │ └── README.md │ ├── package.json │ ├── README.md │ ├── rollup.config.js │ ├── scripts │ │ ├── postinstall.js │ │ ├── switch-cli.js │ │ └── utils.js │ ├── src │ │ ├── __tests__ │ │ │ ├── expression.scope.spec.ts │ │ │ ├── field.spec.ts │ │ │ ├── form.spec.ts │ │ │ ├── schema.json.spec.ts │ │ │ ├── schema.markup.spec.ts │ │ │ ├── shared.spec.ts │ │ │ └── utils.spec.ts │ │ ├── components │ │ │ ├── ArrayField.ts │ │ │ ├── ExpressionScope.ts │ │ │ ├── Field.ts │ │ │ ├── FormConsumer.ts │ │ │ ├── FormProvider.ts │ │ │ ├── index.ts │ │ │ ├── ObjectField.ts │ │ │ ├── ReactiveField.ts │ │ │ ├── RecursionField.ts │ │ │ ├── SchemaField.ts │ │ │ └── VoidField.ts │ │ ├── global.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useAttach.ts │ │ │ ├── useField.ts │ │ │ ├── useFieldSchema.ts │ │ │ ├── useForm.ts │ │ │ ├── useFormEffects.ts │ │ │ ├── useInjectionCleaner.ts │ │ │ └── useParentForm.ts │ │ ├── index.ts │ │ ├── shared │ │ │ ├── connect.ts │ │ │ ├── context.ts │ │ │ ├── createForm.ts │ │ │ ├── fragment.ts │ │ │ ├── h.ts │ │ │ └── index.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── formatVNodeData.ts │ │ │ ├── getFieldProps.ts │ │ │ ├── getRawComponent.ts │ │ │ └── resolveSchemaProps.ts │ │ └── vue2-components.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tsconfig.types.json ├── README.md ├── README.zh-cn.md ├── scripts │ ├── build-style │ │ ├── buildAllStyles.ts │ │ ├── copy.ts │ │ ├── helper.ts │ │ └── index.ts │ └── rollup.base.js ├── tsconfig.build.json ├── tsconfig.jest.json ├── tsconfig.json └── yarn.lock ``` # Files -------------------------------------------------------------------------------- /docs/guide/scenes/edit-detail.zh-CN.md: -------------------------------------------------------------------------------- ```markdown 1 | # 编辑详情 2 | 3 | ## 编辑 4 | 5 | #### Markup Schema 案例 6 | 7 | ```tsx 8 | import React, { useState, useEffect } from 'react' 9 | import { createForm } from '@formily/core' 10 | import { createSchemaField } from '@formily/react' 11 | import { 12 | Form, 13 | FormItem, 14 | FormLayout, 15 | Input, 16 | Select, 17 | Cascader, 18 | DatePicker, 19 | Submit, 20 | FormGrid, 21 | Upload, 22 | ArrayItems, 23 | Editable, 24 | FormButtonGroup, 25 | } from '@formily/antd' 26 | import { action } from '@formily/reactive' 27 | import { Card, Button, Spin } from 'antd' 28 | import { UploadOutlined } from '@ant-design/icons' 29 | 30 | const form = createForm({ 31 | validateFirst: true, 32 | }) 33 | 34 | const IDUpload = (props) => { 35 | return ( 36 | <Upload 37 | {...props} 38 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 39 | headers={{ 40 | authorization: 'authorization-text', 41 | }} 42 | > 43 | <Button icon={<UploadOutlined />}>上传复印件</Button> 44 | </Upload> 45 | ) 46 | } 47 | 48 | const SchemaField = createSchemaField({ 49 | components: { 50 | FormItem, 51 | FormGrid, 52 | FormLayout, 53 | Input, 54 | DatePicker, 55 | Cascader, 56 | Select, 57 | IDUpload, 58 | ArrayItems, 59 | Editable, 60 | }, 61 | scope: { 62 | fetchAddress: (field) => { 63 | const transform = (data = {}) => { 64 | return Object.entries(data).reduce((buf, [key, value]) => { 65 | if (typeof value === 'string') 66 | return buf.concat({ 67 | label: value, 68 | value: key, 69 | }) 70 | const { name, code, cities, districts } = value 71 | const _cities = transform(cities) 72 | const _districts = transform(districts) 73 | return buf.concat({ 74 | label: name, 75 | value: code, 76 | children: _cities.length 77 | ? _cities 78 | : _districts.length 79 | ? _districts 80 | : undefined, 81 | }) 82 | }, []) 83 | } 84 | 85 | field.loading = true 86 | fetch('//unpkg.com/china-location/dist/location.json') 87 | .then((res) => res.json()) 88 | .then( 89 | action.bound((data) => { 90 | field.dataSource = transform(data) 91 | field.loading = false 92 | }) 93 | ) 94 | }, 95 | }, 96 | }) 97 | 98 | export default () => { 99 | const [loading, setLoading] = useState(true) 100 | useEffect(() => { 101 | setTimeout(() => { 102 | form.setInitialValues({ 103 | username: 'Aston Martin', 104 | firstName: 'Aston', 105 | lastName: 'Martin', 106 | email: '[email protected]', 107 | gender: 1, 108 | birthday: '1836-01-03', 109 | address: ['110000', '110000', '110101'], 110 | idCard: [ 111 | { 112 | name: 'this is image', 113 | thumbUrl: 114 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 115 | uid: 'rc-upload-1615825692847-2', 116 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 117 | }, 118 | ], 119 | contacts: [ 120 | { name: '张三', phone: '13245633378', email: '[email protected]' }, 121 | { name: '李四', phone: '16873452678', email: '[email protected]' }, 122 | ], 123 | }) 124 | setLoading(false) 125 | }, 2000) 126 | }, []) 127 | return ( 128 | <div 129 | style={{ 130 | display: 'flex', 131 | justifyContent: 'center', 132 | background: '#eee', 133 | padding: '40px 0', 134 | }} 135 | > 136 | <Card title="编辑用户" style={{ width: 620 }}> 137 | <Spin spinning={loading}> 138 | <Form 139 | form={form} 140 | labelCol={5} 141 | wrapperCol={16} 142 | onAutoSubmit={console.log} 143 | > 144 | <SchemaField> 145 | <SchemaField.String 146 | name="username" 147 | title="用户名" 148 | required 149 | x-decorator="FormItem" 150 | x-component="Input" 151 | /> 152 | <SchemaField.Void 153 | title="姓名" 154 | x-decorator="FormItem" 155 | x-decorator-props={{ 156 | asterisk: true, 157 | feedbackLayout: 'none', 158 | }} 159 | x-component="FormGrid" 160 | > 161 | <SchemaField.String 162 | name="firstName" 163 | x-decorator="FormItem" 164 | x-component="Input" 165 | x-component-props={{ 166 | placeholder: '姓', 167 | }} 168 | required 169 | /> 170 | <SchemaField.String 171 | name="lastName" 172 | x-decorator="FormItem" 173 | x-component="Input" 174 | x-component-props={{ 175 | placeholder: '名', 176 | }} 177 | required 178 | /> 179 | </SchemaField.Void> 180 | <SchemaField.String 181 | name="email" 182 | title="邮箱" 183 | required 184 | x-validator="email" 185 | x-decorator="FormItem" 186 | x-component="Input" 187 | /> 188 | <SchemaField.String 189 | name="gender" 190 | title="性别" 191 | x-decorator="FormItem" 192 | x-component="Select" 193 | enum={[ 194 | { 195 | label: '男', 196 | value: 1, 197 | }, 198 | { 199 | label: '女', 200 | value: 2, 201 | }, 202 | { 203 | label: '第三性别', 204 | value: 3, 205 | }, 206 | ]} 207 | required 208 | /> 209 | <SchemaField.String 210 | name="birthday" 211 | title="生日" 212 | required 213 | x-decorator="FormItem" 214 | x-component="DatePicker" 215 | /> 216 | <SchemaField.String 217 | name="address" 218 | title="地址" 219 | required 220 | x-decorator="FormItem" 221 | x-component="Cascader" 222 | x-reactions="{{fetchAddress}}" 223 | /> 224 | <SchemaField.String 225 | name="idCard" 226 | title="身份证复印件" 227 | required 228 | x-decorator="FormItem" 229 | x-component="IDUpload" 230 | /> 231 | <SchemaField.Array 232 | name="contacts" 233 | title="联系人信息" 234 | required 235 | x-decorator="FormItem" 236 | x-component="ArrayItems" 237 | > 238 | <SchemaField.Object x-component="ArrayItems.Item"> 239 | <SchemaField.Void 240 | x-decorator="FormItem" 241 | x-component="ArrayItems.SortHandle" 242 | /> 243 | <SchemaField.Void 244 | name="popover" 245 | title="维护联系人信息" 246 | x-decorator="Editable.Popover" 247 | x-component="FormLayout" 248 | x-component-props={{ 249 | layout: 'vertical', 250 | }} 251 | x-reactions={[ 252 | { 253 | fulfill: { 254 | schema: { 255 | title: '{{$self.query(".name").value() }}', 256 | }, 257 | }, 258 | }, 259 | ]} 260 | > 261 | <SchemaField.String 262 | name="name" 263 | required 264 | title="姓名" 265 | x-decorator="FormItem" 266 | x-component="Input" 267 | x-component-props={{ 268 | style: { 269 | width: 300, 270 | }, 271 | }} 272 | /> 273 | <SchemaField.String 274 | name="email" 275 | title="邮箱" 276 | x-validator={[{ required: true }, 'email']} 277 | x-decorator="FormItem" 278 | x-component="Input" 279 | x-component-props={{ 280 | style: { 281 | width: 300, 282 | }, 283 | }} 284 | /> 285 | <SchemaField.String 286 | name="phone" 287 | required 288 | title="手机号" 289 | x-validator="phone" 290 | x-decorator="FormItem" 291 | x-component="Input" 292 | x-component-props={{ 293 | style: { 294 | width: 300, 295 | }, 296 | }} 297 | /> 298 | </SchemaField.Void> 299 | <SchemaField.Void 300 | x-decorator="FormItem" 301 | x-component="ArrayItems.Remove" 302 | /> 303 | </SchemaField.Object> 304 | <SchemaField.Void 305 | x-component="ArrayItems.Addition" 306 | title="新增联系人" 307 | /> 308 | </SchemaField.Array> 309 | </SchemaField> 310 | <FormButtonGroup.FormItem> 311 | <Submit block size="large"> 312 | 提交 313 | </Submit> 314 | </FormButtonGroup.FormItem> 315 | </Form> 316 | </Spin> 317 | </Card> 318 | </div> 319 | ) 320 | } 321 | ``` 322 | 323 | #### JSON Schema 案例 324 | 325 | ```tsx 326 | import React, { useState, useEffect } from 'react' 327 | import { createForm } from '@formily/core' 328 | import { createSchemaField } from '@formily/react' 329 | import { 330 | Form, 331 | FormItem, 332 | FormLayout, 333 | Input, 334 | Select, 335 | Cascader, 336 | DatePicker, 337 | Submit, 338 | FormGrid, 339 | Upload, 340 | ArrayItems, 341 | Editable, 342 | FormButtonGroup, 343 | } from '@formily/antd' 344 | import { action } from '@formily/reactive' 345 | import { Card, Button, Spin } from 'antd' 346 | import { UploadOutlined } from '@ant-design/icons' 347 | 348 | const form = createForm({ 349 | validateFirst: true, 350 | }) 351 | 352 | const IDUpload = (props) => { 353 | return ( 354 | <Upload 355 | {...props} 356 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 357 | headers={{ 358 | authorization: 'authorization-text', 359 | }} 360 | > 361 | <Button icon={<UploadOutlined />}>上传复印件</Button> 362 | </Upload> 363 | ) 364 | } 365 | 366 | const SchemaField = createSchemaField({ 367 | components: { 368 | FormItem, 369 | FormGrid, 370 | FormLayout, 371 | Input, 372 | DatePicker, 373 | Cascader, 374 | Select, 375 | IDUpload, 376 | ArrayItems, 377 | Editable, 378 | }, 379 | scope: { 380 | fetchAddress: (field) => { 381 | const transform = (data = {}) => { 382 | return Object.entries(data).reduce((buf, [key, value]) => { 383 | if (typeof value === 'string') 384 | return buf.concat({ 385 | label: value, 386 | value: key, 387 | }) 388 | const { name, code, cities, districts } = value 389 | const _cities = transform(cities) 390 | const _districts = transform(districts) 391 | return buf.concat({ 392 | label: name, 393 | value: code, 394 | children: _cities.length 395 | ? _cities 396 | : _districts.length 397 | ? _districts 398 | : undefined, 399 | }) 400 | }, []) 401 | } 402 | 403 | field.loading = true 404 | fetch('//unpkg.com/china-location/dist/location.json') 405 | .then((res) => res.json()) 406 | .then( 407 | action.bound((data) => { 408 | field.dataSource = transform(data) 409 | field.loading = false 410 | }) 411 | ) 412 | }, 413 | }, 414 | }) 415 | 416 | const schema = { 417 | type: 'object', 418 | properties: { 419 | username: { 420 | type: 'string', 421 | title: '用户名', 422 | required: true, 423 | 'x-decorator': 'FormItem', 424 | 'x-component': 'Input', 425 | }, 426 | name: { 427 | type: 'void', 428 | title: '姓名', 429 | 'x-decorator': 'FormItem', 430 | 'x-decorator-props': { 431 | asterisk: true, 432 | feedbackLayout: 'none', 433 | }, 434 | 'x-component': 'FormGrid', 435 | properties: { 436 | firstName: { 437 | type: 'string', 438 | required: true, 439 | 'x-decorator': 'FormItem', 440 | 'x-component': 'Input', 441 | 'x-component-props': { 442 | placeholder: '姓', 443 | }, 444 | }, 445 | lastName: { 446 | type: 'string', 447 | required: true, 448 | 'x-decorator': 'FormItem', 449 | 'x-component': 'Input', 450 | 'x-component-props': { 451 | placeholder: '名', 452 | }, 453 | }, 454 | }, 455 | }, 456 | email: { 457 | type: 'string', 458 | title: '邮箱', 459 | required: true, 460 | 'x-decorator': 'FormItem', 461 | 'x-component': 'Input', 462 | 'x-validator': 'email', 463 | }, 464 | gender: { 465 | type: 'string', 466 | title: '性别', 467 | enum: [ 468 | { 469 | label: '男', 470 | value: 1, 471 | }, 472 | { 473 | label: '女', 474 | value: 2, 475 | }, 476 | { 477 | label: '第三性别', 478 | value: 3, 479 | }, 480 | ], 481 | 'x-decorator': 'FormItem', 482 | 'x-component': 'Select', 483 | }, 484 | birthday: { 485 | type: 'string', 486 | required: true, 487 | title: '生日', 488 | 'x-decorator': 'FormItem', 489 | 'x-component': 'DatePicker', 490 | }, 491 | address: { 492 | type: 'string', 493 | required: true, 494 | title: '地址', 495 | 'x-decorator': 'FormItem', 496 | 'x-component': 'Cascader', 497 | 'x-reactions': '{{fetchAddress}}', 498 | }, 499 | idCard: { 500 | type: 'string', 501 | required: true, 502 | title: '身份证复印件', 503 | 'x-decorator': 'FormItem', 504 | 'x-component': 'IDUpload', 505 | }, 506 | contacts: { 507 | type: 'array', 508 | required: true, 509 | title: '联系人信息', 510 | 'x-decorator': 'FormItem', 511 | 'x-component': 'ArrayItems', 512 | items: { 513 | type: 'object', 514 | 'x-component': 'ArrayItems.Item', 515 | properties: { 516 | sort: { 517 | type: 'void', 518 | 'x-decorator': 'FormItem', 519 | 'x-component': 'ArrayItems.SortHandle', 520 | }, 521 | popover: { 522 | type: 'void', 523 | title: '完善联系人信息', 524 | 'x-decorator': 'Editable.Popover', 525 | 'x-component': 'FormLayout', 526 | 'x-component-props': { 527 | layout: 'vertical', 528 | }, 529 | 'x-reactions': [ 530 | { 531 | fulfill: { 532 | schema: { 533 | title: '{{$self.query(".name").value() }}', 534 | }, 535 | }, 536 | }, 537 | ], 538 | properties: { 539 | name: { 540 | type: 'string', 541 | title: '姓名', 542 | required: true, 543 | 'x-decorator': 'FormItem', 544 | 'x-component': 'Input', 545 | 'x-component-props': { 546 | style: { 547 | width: 300, 548 | }, 549 | }, 550 | }, 551 | email: { 552 | type: 'string', 553 | title: '邮箱', 554 | 'x-decorator': 'FormItem', 555 | 'x-component': 'Input', 556 | 'x-validator': [{ required: true }, 'email'], 557 | 'x-component-props': { 558 | style: { 559 | width: 300, 560 | }, 561 | }, 562 | }, 563 | phone: { 564 | type: 'string', 565 | title: '手机号', 566 | 'x-decorator': 'FormItem', 567 | 'x-component': 'Input', 568 | 'x-validator': [{ required: true }, 'phone'], 569 | 'x-component-props': { 570 | style: { 571 | width: 300, 572 | }, 573 | }, 574 | }, 575 | }, 576 | }, 577 | remove: { 578 | type: 'void', 579 | 'x-decorator': 'FormItem', 580 | 'x-component': 'ArrayItems.Remove', 581 | }, 582 | }, 583 | }, 584 | properties: { 585 | addition: { 586 | type: 'void', 587 | title: '新增联系人', 588 | 'x-component': 'ArrayItems.Addition', 589 | }, 590 | }, 591 | }, 592 | }, 593 | } 594 | 595 | export default () => { 596 | const [loading, setLoading] = useState(true) 597 | useEffect(() => { 598 | setTimeout(() => { 599 | form.setInitialValues({ 600 | username: 'Aston Martin', 601 | firstName: 'Aston', 602 | lastName: 'Martin', 603 | email: '[email protected]', 604 | gender: 1, 605 | birthday: '1836-01-03', 606 | address: ['110000', '110000', '110101'], 607 | idCard: [ 608 | { 609 | name: 'this is image', 610 | thumbUrl: 611 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 612 | uid: 'rc-upload-1615825692847-2', 613 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 614 | }, 615 | ], 616 | contacts: [ 617 | { name: '张三', phone: '13245633378', email: '[email protected]' }, 618 | { name: '李四', phone: '16873452678', email: '[email protected]' }, 619 | ], 620 | }) 621 | setLoading(false) 622 | }, 2000) 623 | }, []) 624 | return ( 625 | <div 626 | style={{ 627 | display: 'flex', 628 | justifyContent: 'center', 629 | background: '#eee', 630 | padding: '40px 0', 631 | }} 632 | > 633 | <Card title="编辑用户" style={{ width: 620 }}> 634 | <Spin spinning={loading}> 635 | <Form 636 | form={form} 637 | labelCol={5} 638 | wrapperCol={16} 639 | onAutoSubmit={console.log} 640 | > 641 | <SchemaField schema={schema} /> 642 | <FormButtonGroup.FormItem> 643 | <Submit block size="large"> 644 | 提交 645 | </Submit> 646 | </FormButtonGroup.FormItem> 647 | </Form> 648 | </Spin> 649 | </Card> 650 | </div> 651 | ) 652 | } 653 | ``` 654 | 655 | #### 纯 JSX 案例 656 | 657 | ```tsx 658 | import React, { useState, useEffect } from 'react' 659 | import { createForm } from '@formily/core' 660 | import { Field, VoidField, ArrayField } from '@formily/react' 661 | import { 662 | Form, 663 | FormItem, 664 | FormLayout, 665 | Input, 666 | Select, 667 | Cascader, 668 | DatePicker, 669 | Submit, 670 | FormGrid, 671 | Upload, 672 | ArrayBase, 673 | Editable, 674 | FormButtonGroup, 675 | } from '@formily/antd' 676 | import { action } from '@formily/reactive' 677 | import { Card, Button, Spin } from 'antd' 678 | import { UploadOutlined } from '@ant-design/icons' 679 | import './index.less' 680 | 681 | const form = createForm({ 682 | validateFirst: true, 683 | }) 684 | 685 | const IDUpload = (props) => { 686 | return ( 687 | <Upload 688 | {...props} 689 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 690 | headers={{ 691 | authorization: 'authorization-text', 692 | }} 693 | > 694 | <Button icon={<UploadOutlined />}>上传复印件</Button> 695 | </Upload> 696 | ) 697 | } 698 | 699 | const fetchAddress = (field) => { 700 | const transform = (data = {}) => { 701 | return Object.entries(data).reduce((buf, [key, value]) => { 702 | if (typeof value === 'string') 703 | return buf.concat({ 704 | label: value, 705 | value: key, 706 | }) 707 | const { name, code, cities, districts } = value 708 | const _cities = transform(cities) 709 | const _districts = transform(districts) 710 | return buf.concat({ 711 | label: name, 712 | value: code, 713 | children: _cities.length 714 | ? _cities 715 | : _districts.length 716 | ? _districts 717 | : undefined, 718 | }) 719 | }, []) 720 | } 721 | 722 | field.loading = true 723 | fetch('//unpkg.com/china-location/dist/location.json') 724 | .then((res) => res.json()) 725 | .then( 726 | action.bound((data) => { 727 | field.dataSource = transform(data) 728 | field.loading = false 729 | }) 730 | ) 731 | } 732 | 733 | export default () => { 734 | const [loading, setLoading] = useState(true) 735 | useEffect(() => { 736 | setTimeout(() => { 737 | form.setInitialValues({ 738 | username: 'Aston Martin', 739 | firstName: 'Aston', 740 | lastName: 'Martin', 741 | email: '[email protected]', 742 | gender: 1, 743 | birthday: '1836-01-03', 744 | address: ['110000', '110000', '110101'], 745 | idCard: [ 746 | { 747 | name: 'this is image', 748 | thumbUrl: 749 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 750 | uid: 'rc-upload-1615825692847-2', 751 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 752 | }, 753 | ], 754 | contacts: [ 755 | { name: '张三', phone: '13245633378', email: '[email protected]' }, 756 | { name: '李四', phone: '16873452678', email: '[email protected]' }, 757 | ], 758 | }) 759 | setLoading(false) 760 | }, 2000) 761 | }, []) 762 | return ( 763 | <div 764 | style={{ 765 | display: 'flex', 766 | justifyContent: 'center', 767 | background: '#eee', 768 | padding: '40px 0', 769 | }} 770 | > 771 | <Card title="编辑用户" style={{ width: 620 }}> 772 | <Spin spinning={loading}> 773 | <Form 774 | form={form} 775 | labelCol={5} 776 | wrapperCol={16} 777 | onAutoSubmit={console.log} 778 | > 779 | <Field 780 | name="username" 781 | title="用户名" 782 | required 783 | decorator={[FormItem]} 784 | component={[Input]} 785 | /> 786 | <VoidField 787 | name="name" 788 | title="姓名" 789 | decorator={[ 790 | FormItem, 791 | { 792 | asterisk: true, 793 | feedbackLayout: 'none', 794 | }, 795 | ]} 796 | component={[FormGrid]} 797 | > 798 | <Field 799 | name="firstName" 800 | decorator={[FormItem]} 801 | component={[ 802 | Input, 803 | { 804 | placeholder: '姓', 805 | }, 806 | ]} 807 | required 808 | /> 809 | <Field 810 | name="lastName" 811 | decorator={[FormItem]} 812 | component={[ 813 | Input, 814 | { 815 | placeholder: '名', 816 | }, 817 | ]} 818 | required 819 | /> 820 | </VoidField> 821 | <Field 822 | name="email" 823 | title="邮箱" 824 | required 825 | validator="email" 826 | decorator={[FormItem]} 827 | component={[Input]} 828 | /> 829 | <Field 830 | name="gender" 831 | title="性别" 832 | decorator={[FormItem]} 833 | component={[Select]} 834 | dataSource={[ 835 | { 836 | label: '男', 837 | value: 1, 838 | }, 839 | { 840 | label: '女', 841 | value: 2, 842 | }, 843 | { 844 | label: '第三性别', 845 | value: 3, 846 | }, 847 | ]} 848 | required 849 | /> 850 | <Field 851 | name="birthday" 852 | title="生日" 853 | required 854 | decorator={[FormItem]} 855 | component={[DatePicker]} 856 | /> 857 | <Field 858 | name="address" 859 | title="地址" 860 | required 861 | decorator={[FormItem]} 862 | component={[Cascader]} 863 | reactions={fetchAddress} 864 | /> 865 | <Field 866 | name="idCard" 867 | title="身份证复印件" 868 | required 869 | decorator={[FormItem]} 870 | component={[IDUpload]} 871 | /> 872 | <ArrayField 873 | name="contacts" 874 | title="联系人信息" 875 | decorator={[FormItem]} 876 | > 877 | {(field) => ( 878 | <ArrayBase> 879 | {field.value?.map((item, index) => ( 880 | <div key={index} className="array-items-item"> 881 | <Field 882 | name={`${index}`} 883 | title="完善联系人信息" 884 | component={[Editable.Popover]} 885 | reactions={(field) => { 886 | field.title = 887 | field.query('.[].name').value() || field.title 888 | }} 889 | > 890 | <VoidField 891 | name="layout" 892 | component={[FormLayout, { layout: 'vertical' }]} 893 | > 894 | <Field 895 | name="name" 896 | title="姓名" 897 | required 898 | decorator={[FormItem]} 899 | component={[ 900 | Input, 901 | { 902 | style: { 903 | width: 300, 904 | }, 905 | }, 906 | ]} 907 | /> 908 | <Field 909 | name="email" 910 | title="邮箱" 911 | required 912 | validator="email" 913 | decorator={[FormItem]} 914 | component={[ 915 | Input, 916 | { 917 | style: { 918 | width: 300, 919 | }, 920 | }, 921 | ]} 922 | /> 923 | <Field 924 | name="phone" 925 | title="手机号" 926 | required 927 | validator="phone" 928 | decorator={[FormItem]} 929 | component={[ 930 | Input, 931 | { 932 | style: { 933 | width: 300, 934 | }, 935 | }, 936 | ]} 937 | /> 938 | </VoidField> 939 | </Field> 940 | <FormItem.BaseItem> 941 | <ArrayBase.Remove index={index} /> 942 | <ArrayBase.MoveDown index={index} /> 943 | <ArrayBase.MoveUp index={index} /> 944 | </FormItem.BaseItem> 945 | </div> 946 | ))} 947 | <ArrayBase.Addition title="新增联系人" /> 948 | </ArrayBase> 949 | )} 950 | </ArrayField> 951 | <FormButtonGroup.FormItem> 952 | <Submit block size="large"> 953 | 提交 954 | </Submit> 955 | </FormButtonGroup.FormItem> 956 | </Form> 957 | </Spin> 958 | </Card> 959 | </div> 960 | ) 961 | } 962 | ``` 963 | 964 | ## 详情 965 | 966 | #### Markup Schema 案例 967 | 968 | ```tsx 969 | import React, { useState, useEffect } from 'react' 970 | import { createForm } from '@formily/core' 971 | import { createSchemaField, useField } from '@formily/react' 972 | import { 973 | Form, 974 | FormItem, 975 | FormLayout, 976 | Input, 977 | Select, 978 | Cascader, 979 | DatePicker, 980 | FormGrid, 981 | Upload, 982 | ArrayItems, 983 | Editable, 984 | PreviewText, 985 | } from '@formily/antd' 986 | import { action } from '@formily/reactive' 987 | import { Card, Button, Spin } from 'antd' 988 | import { UploadOutlined } from '@ant-design/icons' 989 | 990 | const form = createForm({ 991 | readPretty: true, 992 | validateFirst: true, 993 | }) 994 | 995 | const IDUpload = (props) => { 996 | const field = useField() 997 | return ( 998 | <Upload 999 | {...props} 1000 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 1001 | headers={{ 1002 | authorization: 'authorization-text', 1003 | }} 1004 | > 1005 | {field.editable && <Button icon={<UploadOutlined />}>上传复印件</Button>} 1006 | </Upload> 1007 | ) 1008 | } 1009 | 1010 | const SchemaField = createSchemaField({ 1011 | components: { 1012 | FormItem, 1013 | FormGrid, 1014 | FormLayout, 1015 | Input, 1016 | DatePicker, 1017 | Cascader, 1018 | Select, 1019 | IDUpload, 1020 | ArrayItems, 1021 | Editable, 1022 | }, 1023 | scope: { 1024 | fetchAddress: (field) => { 1025 | const transform = (data = {}) => { 1026 | return Object.entries(data).reduce((buf, [key, value]) => { 1027 | if (typeof value === 'string') 1028 | return buf.concat({ 1029 | label: value, 1030 | value: key, 1031 | }) 1032 | const { name, code, cities, districts } = value 1033 | const _cities = transform(cities) 1034 | const _districts = transform(districts) 1035 | return buf.concat({ 1036 | label: name, 1037 | value: code, 1038 | children: _cities.length 1039 | ? _cities 1040 | : _districts.length 1041 | ? _districts 1042 | : undefined, 1043 | }) 1044 | }, []) 1045 | } 1046 | 1047 | field.loading = true 1048 | fetch('//unpkg.com/china-location/dist/location.json') 1049 | .then((res) => res.json()) 1050 | .then( 1051 | action.bound((data) => { 1052 | field.dataSource = transform(data) 1053 | field.loading = false 1054 | }) 1055 | ) 1056 | }, 1057 | }, 1058 | }) 1059 | 1060 | export default () => { 1061 | const [loading, setLoading] = useState(true) 1062 | useEffect(() => { 1063 | setTimeout(() => { 1064 | form.setInitialValues({ 1065 | username: 'Aston Martin', 1066 | firstName: 'Aston', 1067 | lastName: 'Martin', 1068 | email: '[email protected]', 1069 | gender: 1, 1070 | birthday: '1836-01-03', 1071 | address: ['110000', '110000', '110101'], 1072 | idCard: [ 1073 | { 1074 | name: 'this is image', 1075 | thumbUrl: 1076 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1077 | uid: 'rc-upload-1615825692847-2', 1078 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1079 | }, 1080 | ], 1081 | contacts: [ 1082 | { name: '张三', phone: '13245633378', email: '[email protected]' }, 1083 | { name: '李四', phone: '16873452678', email: '[email protected]' }, 1084 | ], 1085 | }) 1086 | setLoading(false) 1087 | }, 2000) 1088 | }, []) 1089 | return ( 1090 | <div 1091 | style={{ 1092 | display: 'flex', 1093 | justifyContent: 'center', 1094 | background: '#eee', 1095 | padding: '40px 0', 1096 | }} 1097 | > 1098 | <PreviewText.Placeholder value="-"> 1099 | <Card title="用户详情" style={{ width: 620 }}> 1100 | <Spin spinning={loading}> 1101 | <Form 1102 | form={form} 1103 | labelCol={5} 1104 | wrapperCol={16} 1105 | onAutoSubmit={console.log} 1106 | > 1107 | <SchemaField> 1108 | <SchemaField.String 1109 | name="username" 1110 | title="用户名" 1111 | required 1112 | x-decorator="FormItem" 1113 | x-component="Input" 1114 | /> 1115 | <SchemaField.Void 1116 | title="姓名" 1117 | x-decorator="FormItem" 1118 | x-decorator-props={{ 1119 | feedbackLayout: 'none', 1120 | }} 1121 | x-component="FormGrid" 1122 | > 1123 | <SchemaField.String 1124 | name="firstName" 1125 | x-decorator="FormItem" 1126 | x-component="Input" 1127 | x-component-props={{ 1128 | placeholder: '姓', 1129 | }} 1130 | required 1131 | /> 1132 | <SchemaField.String 1133 | name="lastName" 1134 | x-decorator="FormItem" 1135 | x-component="Input" 1136 | x-component-props={{ 1137 | placeholder: '名', 1138 | }} 1139 | required 1140 | /> 1141 | </SchemaField.Void> 1142 | <SchemaField.String 1143 | name="email" 1144 | title="邮箱" 1145 | required 1146 | x-validator="email" 1147 | x-decorator="FormItem" 1148 | x-component="Input" 1149 | /> 1150 | <SchemaField.String 1151 | name="gender" 1152 | title="性别" 1153 | x-decorator="FormItem" 1154 | x-component="Select" 1155 | enum={[ 1156 | { 1157 | label: '男', 1158 | value: 1, 1159 | }, 1160 | { 1161 | label: '女', 1162 | value: 2, 1163 | }, 1164 | { 1165 | label: '第三性别', 1166 | value: 3, 1167 | }, 1168 | ]} 1169 | required 1170 | /> 1171 | <SchemaField.String 1172 | name="birthday" 1173 | title="生日" 1174 | required 1175 | x-decorator="FormItem" 1176 | x-component="DatePicker" 1177 | /> 1178 | <SchemaField.String 1179 | name="address" 1180 | title="地址" 1181 | required 1182 | x-decorator="FormItem" 1183 | x-component="Cascader" 1184 | x-reactions="{{fetchAddress}}" 1185 | /> 1186 | <SchemaField.String 1187 | name="idCard" 1188 | title="身份证复印件" 1189 | required 1190 | x-decorator="FormItem" 1191 | x-component="IDUpload" 1192 | /> 1193 | <SchemaField.Array 1194 | name="contacts" 1195 | title="联系人信息" 1196 | required 1197 | x-decorator="FormItem" 1198 | x-component="ArrayItems" 1199 | > 1200 | <SchemaField.Object x-component="ArrayItems.Item"> 1201 | <SchemaField.Void 1202 | x-decorator="FormItem" 1203 | x-component="ArrayItems.SortHandle" 1204 | /> 1205 | <SchemaField.Void 1206 | name="popover" 1207 | title="维护联系人信息" 1208 | x-decorator="Editable.Popover" 1209 | x-component="FormLayout" 1210 | x-component-props={{ 1211 | layout: 'vertical', 1212 | }} 1213 | x-reactions={[ 1214 | { 1215 | fulfill: { 1216 | schema: { 1217 | title: '{{$self.query(".name").value() }}', 1218 | }, 1219 | }, 1220 | }, 1221 | ]} 1222 | > 1223 | <SchemaField.String 1224 | name="name" 1225 | required 1226 | title="姓名" 1227 | x-decorator="FormItem" 1228 | x-component="Input" 1229 | x-component-props={{ 1230 | style: { 1231 | width: 300, 1232 | }, 1233 | }} 1234 | /> 1235 | <SchemaField.String 1236 | name="email" 1237 | title="邮箱" 1238 | x-validator={[{ required: true }, 'email']} 1239 | x-decorator="FormItem" 1240 | x-component="Input" 1241 | x-component-props={{ 1242 | style: { 1243 | width: 300, 1244 | }, 1245 | }} 1246 | /> 1247 | <SchemaField.String 1248 | name="phone" 1249 | required 1250 | title="手机号" 1251 | x-validator="phone" 1252 | x-decorator="FormItem" 1253 | x-component="Input" 1254 | x-component-props={{ 1255 | style: { 1256 | width: 300, 1257 | }, 1258 | }} 1259 | /> 1260 | </SchemaField.Void> 1261 | <SchemaField.Void 1262 | x-decorator="FormItem" 1263 | x-component="ArrayItems.Remove" 1264 | /> 1265 | </SchemaField.Object> 1266 | <SchemaField.Void 1267 | x-component="ArrayItems.Addition" 1268 | title="新增联系人" 1269 | /> 1270 | </SchemaField.Array> 1271 | </SchemaField> 1272 | </Form> 1273 | </Spin> 1274 | </Card> 1275 | </PreviewText.Placeholder> 1276 | </div> 1277 | ) 1278 | } 1279 | ``` 1280 | 1281 | #### JSON Schema 案例 1282 | 1283 | ```tsx 1284 | import React, { useState, useEffect } from 'react' 1285 | import { createForm } from '@formily/core' 1286 | import { createSchemaField, useField } from '@formily/react' 1287 | import { 1288 | Form, 1289 | FormItem, 1290 | FormLayout, 1291 | Input, 1292 | Select, 1293 | Cascader, 1294 | DatePicker, 1295 | FormGrid, 1296 | Upload, 1297 | ArrayItems, 1298 | Editable, 1299 | PreviewText, 1300 | } from '@formily/antd' 1301 | import { action } from '@formily/reactive' 1302 | import { Card, Button, Spin } from 'antd' 1303 | import { UploadOutlined } from '@ant-design/icons' 1304 | 1305 | const form = createForm({ 1306 | readPretty: true, 1307 | validateFirst: true, 1308 | }) 1309 | 1310 | const IDUpload = (props) => { 1311 | const field = useField() 1312 | return ( 1313 | <Upload 1314 | {...props} 1315 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 1316 | headers={{ 1317 | authorization: 'authorization-text', 1318 | }} 1319 | > 1320 | {field.editable && <Button icon={<UploadOutlined />}>上传复印件</Button>} 1321 | </Upload> 1322 | ) 1323 | } 1324 | 1325 | const SchemaField = createSchemaField({ 1326 | components: { 1327 | FormItem, 1328 | FormGrid, 1329 | FormLayout, 1330 | Input, 1331 | DatePicker, 1332 | Cascader, 1333 | Select, 1334 | IDUpload, 1335 | ArrayItems, 1336 | Editable, 1337 | }, 1338 | scope: { 1339 | fetchAddress: (field) => { 1340 | const transform = (data = {}) => { 1341 | return Object.entries(data).reduce((buf, [key, value]) => { 1342 | if (typeof value === 'string') 1343 | return buf.concat({ 1344 | label: value, 1345 | value: key, 1346 | }) 1347 | const { name, code, cities, districts } = value 1348 | const _cities = transform(cities) 1349 | const _districts = transform(districts) 1350 | return buf.concat({ 1351 | label: name, 1352 | value: code, 1353 | children: _cities.length 1354 | ? _cities 1355 | : _districts.length 1356 | ? _districts 1357 | : undefined, 1358 | }) 1359 | }, []) 1360 | } 1361 | 1362 | field.loading = true 1363 | fetch('//unpkg.com/china-location/dist/location.json') 1364 | .then((res) => res.json()) 1365 | .then( 1366 | action.bound((data) => { 1367 | field.dataSource = transform(data) 1368 | field.loading = false 1369 | }) 1370 | ) 1371 | }, 1372 | }, 1373 | }) 1374 | 1375 | const schema = { 1376 | type: 'object', 1377 | properties: { 1378 | username: { 1379 | type: 'string', 1380 | title: '用户名', 1381 | required: true, 1382 | 'x-decorator': 'FormItem', 1383 | 'x-component': 'Input', 1384 | }, 1385 | name: { 1386 | type: 'void', 1387 | title: '姓名', 1388 | 'x-decorator': 'FormItem', 1389 | 'x-decorator-props': { 1390 | asterisk: true, 1391 | feedbackLayout: 'none', 1392 | }, 1393 | 'x-component': 'FormGrid', 1394 | properties: { 1395 | firstName: { 1396 | type: 'string', 1397 | required: true, 1398 | 'x-decorator': 'FormItem', 1399 | 'x-component': 'Input', 1400 | 'x-component-props': { 1401 | placeholder: '姓', 1402 | }, 1403 | }, 1404 | lastName: { 1405 | type: 'string', 1406 | required: true, 1407 | 'x-decorator': 'FormItem', 1408 | 'x-component': 'Input', 1409 | 'x-component-props': { 1410 | placeholder: '名', 1411 | }, 1412 | }, 1413 | }, 1414 | }, 1415 | email: { 1416 | type: 'string', 1417 | title: '邮箱', 1418 | required: true, 1419 | 'x-decorator': 'FormItem', 1420 | 'x-component': 'Input', 1421 | 'x-validator': 'email', 1422 | }, 1423 | gender: { 1424 | type: 'string', 1425 | title: '性别', 1426 | enum: [ 1427 | { 1428 | label: '男', 1429 | value: 1, 1430 | }, 1431 | { 1432 | label: '女', 1433 | value: 2, 1434 | }, 1435 | { 1436 | label: '第三性别', 1437 | value: 3, 1438 | }, 1439 | ], 1440 | 'x-decorator': 'FormItem', 1441 | 'x-component': 'Select', 1442 | }, 1443 | birthday: { 1444 | type: 'string', 1445 | required: true, 1446 | title: '生日', 1447 | 'x-decorator': 'FormItem', 1448 | 'x-component': 'DatePicker', 1449 | }, 1450 | address: { 1451 | type: 'string', 1452 | required: true, 1453 | title: '地址', 1454 | 'x-decorator': 'FormItem', 1455 | 'x-component': 'Cascader', 1456 | 'x-reactions': '{{fetchAddress}}', 1457 | }, 1458 | idCard: { 1459 | type: 'string', 1460 | required: true, 1461 | title: '身份证复印件', 1462 | 'x-decorator': 'FormItem', 1463 | 'x-component': 'IDUpload', 1464 | }, 1465 | contacts: { 1466 | type: 'array', 1467 | required: true, 1468 | title: '联系人信息', 1469 | 'x-decorator': 'FormItem', 1470 | 'x-component': 'ArrayItems', 1471 | items: { 1472 | type: 'object', 1473 | 'x-component': 'ArrayItems.Item', 1474 | properties: { 1475 | sort: { 1476 | type: 'void', 1477 | 'x-decorator': 'FormItem', 1478 | 'x-component': 'ArrayItems.SortHandle', 1479 | }, 1480 | popover: { 1481 | type: 'void', 1482 | title: '完善联系人信息', 1483 | 'x-decorator': 'Editable.Popover', 1484 | 'x-component': 'FormLayout', 1485 | 'x-component-props': { 1486 | layout: 'vertical', 1487 | }, 1488 | 'x-reactions': [ 1489 | { 1490 | fulfill: { 1491 | schema: { 1492 | title: '{{$self.query(".name").value() }}', 1493 | }, 1494 | }, 1495 | }, 1496 | ], 1497 | properties: { 1498 | name: { 1499 | type: 'string', 1500 | title: '姓名', 1501 | required: true, 1502 | 'x-decorator': 'FormItem', 1503 | 'x-component': 'Input', 1504 | 'x-component-props': { 1505 | style: { 1506 | width: 300, 1507 | }, 1508 | }, 1509 | }, 1510 | email: { 1511 | type: 'string', 1512 | title: '邮箱', 1513 | 'x-decorator': 'FormItem', 1514 | 'x-component': 'Input', 1515 | 'x-validator': [{ required: true }, 'email'], 1516 | 'x-component-props': { 1517 | style: { 1518 | width: 300, 1519 | }, 1520 | }, 1521 | }, 1522 | phone: { 1523 | type: 'string', 1524 | title: '手机号', 1525 | 'x-decorator': 'FormItem', 1526 | 'x-component': 'Input', 1527 | 'x-validator': [{ required: true }, 'phone'], 1528 | 'x-component-props': { 1529 | style: { 1530 | width: 300, 1531 | }, 1532 | }, 1533 | }, 1534 | }, 1535 | }, 1536 | remove: { 1537 | type: 'void', 1538 | 'x-decorator': 'FormItem', 1539 | 'x-component': 'ArrayItems.Remove', 1540 | }, 1541 | }, 1542 | }, 1543 | properties: { 1544 | addition: { 1545 | type: 'void', 1546 | title: '新增联系人', 1547 | 'x-component': 'ArrayItems.Addition', 1548 | }, 1549 | }, 1550 | }, 1551 | }, 1552 | } 1553 | 1554 | export default () => { 1555 | const [loading, setLoading] = useState(true) 1556 | useEffect(() => { 1557 | setTimeout(() => { 1558 | form.setInitialValues({ 1559 | username: 'Aston Martin', 1560 | firstName: 'Aston', 1561 | lastName: 'Martin', 1562 | email: '[email protected]', 1563 | gender: 1, 1564 | birthday: '1836-01-03', 1565 | address: ['110000', '110000', '110101'], 1566 | idCard: [ 1567 | { 1568 | name: 'this is image', 1569 | thumbUrl: 1570 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1571 | uid: 'rc-upload-1615825692847-2', 1572 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1573 | }, 1574 | ], 1575 | contacts: [ 1576 | { name: '张三', phone: '13245633378', email: '[email protected]' }, 1577 | { name: '李四', phone: '16873452678', email: '[email protected]' }, 1578 | ], 1579 | }) 1580 | setLoading(false) 1581 | }, 2000) 1582 | }, []) 1583 | return ( 1584 | <div 1585 | style={{ 1586 | display: 'flex', 1587 | justifyContent: 'center', 1588 | background: '#eee', 1589 | padding: '40px 0', 1590 | }} 1591 | > 1592 | <PreviewText.Placeholder value="-"> 1593 | <Card title="用户详情" style={{ width: 620 }}> 1594 | <Spin spinning={loading}> 1595 | <Form 1596 | form={form} 1597 | labelCol={5} 1598 | wrapperCol={16} 1599 | onAutoSubmit={console.log} 1600 | > 1601 | <SchemaField schema={schema} /> 1602 | </Form> 1603 | </Spin> 1604 | </Card> 1605 | </PreviewText.Placeholder> 1606 | </div> 1607 | ) 1608 | } 1609 | ``` 1610 | 1611 | #### 纯 JSX 案例 1612 | 1613 | ```tsx 1614 | import React, { useState, useEffect } from 'react' 1615 | import { createForm } from '@formily/core' 1616 | import { Field, VoidField, ArrayField, useField } from '@formily/react' 1617 | import { 1618 | Form, 1619 | FormItem, 1620 | FormLayout, 1621 | Input, 1622 | Select, 1623 | Cascader, 1624 | DatePicker, 1625 | FormGrid, 1626 | ArrayBase, 1627 | Upload, 1628 | PreviewText, 1629 | Editable, 1630 | } from '@formily/antd' 1631 | import { action } from '@formily/reactive' 1632 | import { Card, Button, Spin } from 'antd' 1633 | import { UploadOutlined } from '@ant-design/icons' 1634 | import './index.less' 1635 | 1636 | const form = createForm({ 1637 | validateFirst: true, 1638 | readPretty: true, 1639 | }) 1640 | 1641 | const IDUpload = (props) => { 1642 | const field = useField() 1643 | return ( 1644 | <Upload 1645 | {...props} 1646 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 1647 | headers={{ 1648 | authorization: 'authorization-text', 1649 | }} 1650 | > 1651 | {field.editable && <Button icon={<UploadOutlined />}>上传复印件</Button>} 1652 | </Upload> 1653 | ) 1654 | } 1655 | 1656 | const fetchAddress = (field) => { 1657 | const transform = (data = {}) => { 1658 | return Object.entries(data).reduce((buf, [key, value]) => { 1659 | if (typeof value === 'string') 1660 | return buf.concat({ 1661 | label: value, 1662 | value: key, 1663 | }) 1664 | const { name, code, cities, districts } = value 1665 | const _cities = transform(cities) 1666 | const _districts = transform(districts) 1667 | return buf.concat({ 1668 | label: name, 1669 | value: code, 1670 | children: _cities.length 1671 | ? _cities 1672 | : _districts.length 1673 | ? _districts 1674 | : undefined, 1675 | }) 1676 | }, []) 1677 | } 1678 | 1679 | field.loading = true 1680 | fetch('//unpkg.com/china-location/dist/location.json') 1681 | .then((res) => res.json()) 1682 | .then( 1683 | action.bound((data) => { 1684 | field.dataSource = transform(data) 1685 | field.loading = false 1686 | }) 1687 | ) 1688 | } 1689 | 1690 | export default () => { 1691 | const [loading, setLoading] = useState(true) 1692 | useEffect(() => { 1693 | setTimeout(() => { 1694 | form.setInitialValues({ 1695 | username: 'Aston Martin', 1696 | firstName: 'Aston', 1697 | lastName: 'Martin', 1698 | email: '[email protected]', 1699 | gender: 1, 1700 | birthday: '1836-01-03', 1701 | address: ['110000', '110000', '110101'], 1702 | idCard: [ 1703 | { 1704 | name: 'this is image', 1705 | thumbUrl: 1706 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1707 | uid: 'rc-upload-1615825692847-2', 1708 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1709 | }, 1710 | ], 1711 | contacts: [ 1712 | { name: '张三', phone: '13245633378', email: '[email protected]' }, 1713 | { name: '李四', phone: '16873452678', email: '[email protected]' }, 1714 | ], 1715 | }) 1716 | setLoading(false) 1717 | }, 2000) 1718 | }, []) 1719 | return ( 1720 | <div 1721 | style={{ 1722 | display: 'flex', 1723 | justifyContent: 'center', 1724 | background: '#eee', 1725 | padding: '40px 0', 1726 | }} 1727 | > 1728 | <PreviewText.Placeholder value="-"> 1729 | <Card title="编辑用户" style={{ width: 620 }}> 1730 | <Spin spinning={loading}> 1731 | <Form 1732 | form={form} 1733 | labelCol={5} 1734 | wrapperCol={16} 1735 | onAutoSubmit={console.log} 1736 | > 1737 | <Field 1738 | name="username" 1739 | title="用户名" 1740 | required 1741 | decorator={[FormItem]} 1742 | component={[Input]} 1743 | /> 1744 | <VoidField 1745 | name="name" 1746 | title="姓名" 1747 | decorator={[ 1748 | FormItem, 1749 | { 1750 | feedbackLayout: 'none', 1751 | }, 1752 | ]} 1753 | component={[FormGrid]} 1754 | > 1755 | <Field 1756 | name="firstName" 1757 | decorator={[FormItem]} 1758 | component={[ 1759 | Input, 1760 | { 1761 | placeholder: '姓', 1762 | }, 1763 | ]} 1764 | required 1765 | /> 1766 | <Field 1767 | name="lastName" 1768 | decorator={[FormItem]} 1769 | component={[ 1770 | Input, 1771 | { 1772 | placeholder: '名', 1773 | }, 1774 | ]} 1775 | required 1776 | /> 1777 | </VoidField> 1778 | <Field 1779 | name="email" 1780 | title="邮箱" 1781 | required 1782 | validator="email" 1783 | decorator={[FormItem]} 1784 | component={[Input]} 1785 | /> 1786 | <Field 1787 | name="gender" 1788 | title="性别" 1789 | decorator={[FormItem]} 1790 | component={[Select]} 1791 | dataSource={[ 1792 | { 1793 | label: '男', 1794 | value: 1, 1795 | }, 1796 | { 1797 | label: '女', 1798 | value: 2, 1799 | }, 1800 | { 1801 | label: '第三性别', 1802 | value: 3, 1803 | }, 1804 | ]} 1805 | required 1806 | /> 1807 | <Field 1808 | name="birthday" 1809 | title="生日" 1810 | required 1811 | decorator={[FormItem]} 1812 | component={[DatePicker]} 1813 | /> 1814 | <Field 1815 | name="address" 1816 | title="地址" 1817 | required 1818 | decorator={[FormItem]} 1819 | component={[Cascader]} 1820 | reactions={fetchAddress} 1821 | /> 1822 | <Field 1823 | name="idCard" 1824 | title="身份证复印件" 1825 | required 1826 | decorator={[FormItem]} 1827 | component={[IDUpload]} 1828 | /> 1829 | <ArrayField 1830 | name="contacts" 1831 | title="联系人信息" 1832 | decorator={[FormItem]} 1833 | > 1834 | {(field) => ( 1835 | <ArrayBase> 1836 | {field.value?.map((item, index) => ( 1837 | <div key={index} className="array-items-item"> 1838 | <Field 1839 | name={`${index}`} 1840 | title="完善联系人信息" 1841 | component={[Editable.Popover]} 1842 | reactions={(field) => { 1843 | field.title = 1844 | field.query('.[].name').value() || field.title 1845 | }} 1846 | > 1847 | <VoidField 1848 | name="layout" 1849 | component={[FormLayout, { layout: 'vertical' }]} 1850 | > 1851 | <Field 1852 | name="name" 1853 | title="姓名" 1854 | required 1855 | decorator={[FormItem]} 1856 | component={[ 1857 | Input, 1858 | { 1859 | style: { 1860 | width: 300, 1861 | }, 1862 | }, 1863 | ]} 1864 | /> 1865 | <Field 1866 | name="email" 1867 | title="邮箱" 1868 | required 1869 | validator="email" 1870 | decorator={[FormItem]} 1871 | component={[ 1872 | Input, 1873 | { 1874 | style: { 1875 | width: 300, 1876 | }, 1877 | }, 1878 | ]} 1879 | /> 1880 | <Field 1881 | name="phone" 1882 | title="手机号" 1883 | required 1884 | validator="phone" 1885 | decorator={[FormItem]} 1886 | component={[ 1887 | Input, 1888 | { 1889 | style: { 1890 | width: 300, 1891 | }, 1892 | }, 1893 | ]} 1894 | /> 1895 | </VoidField> 1896 | </Field> 1897 | </div> 1898 | ))} 1899 | <ArrayBase.Addition title="新增联系人" /> 1900 | </ArrayBase> 1901 | )} 1902 | </ArrayField> 1903 | </Form> 1904 | </Spin> 1905 | </Card> 1906 | </PreviewText.Placeholder> 1907 | </div> 1908 | ) 1909 | } 1910 | ``` 1911 | ```