├── .browserslistrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── dev ├── App.vue ├── assets │ └── logo.png ├── descriptor.js └── main.js ├── docs ├── .vuepress │ ├── components │ │ ├── array-demo.vue │ │ ├── base-data-demo.vue │ │ ├── code-demo.vue │ │ ├── custom-component.vue │ │ ├── custom-message.vue │ │ ├── custom-validator.vue │ │ ├── form-demo.vue │ │ ├── form-operation.vue │ │ ├── hashmap-demo.vue │ │ ├── home-demo.vue │ │ └── object-demo.vue │ ├── config.js │ ├── demos │ │ └── cdn.html │ ├── enhanceApp.js │ └── public │ │ ├── css │ │ └── index.css │ │ └── logo.png ├── README.md ├── api │ ├── descriptors │ │ └── README.md │ └── dynamic-form │ │ └── README.md ├── getting-started │ └── README.md ├── guide │ └── README.md ├── i18n │ └── README.md └── zh │ ├── README.md │ ├── api │ ├── descriptors │ │ └── README.md │ └── dynamic-form │ │ └── README.md │ ├── getting-started │ └── README.md │ ├── guide │ └── README.md │ └── i18n │ └── README.md ├── package.json ├── packages ├── dynamic-component │ ├── component.vue │ └── index.js ├── dynamic-form-item │ ├── form-item.vue │ └── index.js ├── dynamic-form │ ├── form.vue │ └── index.js ├── dynamic-input │ ├── index.js │ └── input.vue ├── i18n.js ├── index.js └── utils.js ├── postcss.config.js ├── public ├── index.html └── vue-dynamic-form-component.gif ├── types └── index.d.ts ├── vetur ├── attributes.json └── tags.json ├── vue.config.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /lib 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | '@vue/standard' 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | parserOptions: { 15 | parser: 'babel-eslint' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | lib 5 | docs/.vuepress/dist 6 | 7 | # local env files 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | docs 4 | dev 5 | node_modules 6 | packages 7 | public 8 | 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # Editor directories and files 19 | .idea 20 | .vscode 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | 6 | ## [2.8.1](https://github.com/chenquincy/vue-dynamic-form-component/compare/v2.8.0...v2.8.1) (2020-10-20) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **$dynamic-input:** can set float value with number type ([1b75918](https://github.com/chenquincy/vue-dynamic-form-component/commit/1b75918)), closes [#33](https://github.com/chenquincy/vue-dynamic-form-component/issues/33) 12 | 13 | 14 | 15 | 16 | # [2.8.0](https://github.com/chenquincy/vue-dynamic-form-component/compare/v2.7.0...v2.8.0) (2020-09-17) 17 | 18 | 19 | ### Features 20 | 21 | * support form disabled ([7e51ba4](https://github.com/chenquincy/vue-dynamic-form-component/commit/7e51ba4)) 22 | 23 | 24 | 25 | 26 | # [2.7.0](https://github.com/chenquincy/vue-dynamic-form-component/compare/v2.6.0...v2.7.0) (2020-09-16) 27 | 28 | 29 | ### Features 30 | 31 | * support to custom component for complex type ([b4dc2c5](https://github.com/chenquincy/vue-dynamic-form-component/commit/b4dc2c5)) 32 | 33 | 34 | 35 | 36 | # [2.6.0](https://github.com/chenquincy/vue-dynamic-form-component/compare/v2.5.0...v2.6.0) (2020-07-21) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * upgrade danger deps ([2cc882a](https://github.com/chenquincy/vue-dynamic-form-component/commit/2cc882a)) 42 | 43 | 44 | ### Features 45 | 46 | * add vetur prompt ([7992313](https://github.com/chenquincy/vue-dynamic-form-component/commit/7992313)) 47 | 48 | 49 | 50 | 51 | # [2.2.0](https://github.com/chenquincy/vue-dynamic-form-component/compare/v2.1.0...v2.2.0) (2019-09-01) 52 | 53 | 54 | ### Features 55 | 56 | * support autocomplete ([07c91da](https://github.com/chenquincy/vue-dynamic-form-component/commit/07c91da)) 57 | 58 | 59 | 60 | 61 | ## [2.0.1](https://github.com/chenquincy/vue-dynamic-form-component/compare/v2.0.0...v2.0.1) (2019-08-11) 62 | 63 | 64 | ### Bug Fixes 65 | 66 | * fix language setting and docs import element-ui error. ([1522330](https://github.com/chenquincy/vue-dynamic-form-component/commit/1522330)) 67 | 68 | 69 | 70 | # Change Log 71 | 72 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 73 | 74 | # [2.0.0](https://github.com/chenquincy/vue-dynamic-form-component/compare/v1.1.2...v2.0.0) (2019-08-02) 75 | 76 | 77 | ### Features 78 | 79 | * change vue, element-ui to externals. ([5c46661](https://github.com/chenquincy/vue-dynamic-form-component/commit/5c46661)), closes [#1](https://github.com/chenquincy/vue-dynamic-form-component/issues/1) 80 | 81 | 82 | ### BREAKING CHANGES 83 | 84 | * The default languages change to zh_CN from en_US;Should import vue and element-ui 85 | yourself. 86 | 87 | 88 | 89 | ## [1.1.2](https://github.com/chenquincy/vue-dynamic-form-component/compare/v1.1.1...v1.1.2) (2019-07-17) 90 | 91 | 92 | ### Bug Fixes 93 | 94 | * lodash version upgrade(security issue) ([8f1f31b](https://github.com/chenquincy/vue-dynamic-form-component/commit/8f1f31b)) 95 | 96 | 97 | 98 | ## [1.1.1](https://github.com/chenquincy/vue-dynamic-form-component/compare/v1.1.0...v1.1.1) (2019-06-27) 99 | 100 | 101 | ### Bug Fixes 102 | 103 | * export component error. ([c95b17e](https://github.com/chenquincy/vue-dynamic-form-component/commit/c95b17e)) 104 | 105 | 106 | 107 | # [1.1.0](https://github.com/chenquincy/vue-dynamic-form-component/compare/v1.0.3...v1.1.0) (2019-06-26) 108 | 109 | 110 | ### Features 111 | 112 | * add hidden prop and support custom languages. ([b3419ab](https://github.com/chenquincy/vue-dynamic-form-component/commit/b3419ab)) 113 | 114 | 115 | 116 | ## [1.0.3](https://github.com/chenquincy/vue-dynamic-form-component/compare/v1.0.2...v1.0.3) (2019-06-21) 117 | 118 | 119 | ### Bug Fixes 120 | 121 | * sub dynamic-form-item should pass lang prop, delete button style improve. ([6fe39b6](https://github.com/chenquincy/vue-dynamic-form-component/commit/6fe39b6)) 122 | 123 | 124 | 125 | ## [1.0.2](https://github.com/chenquincy/vue-dynamic-form-component/compare/v1.0.1...v1.0.2) (2019-06-21) 126 | 127 | 128 | ### Bug Fixes 129 | 130 | * label-width compute error ([74d63c6](https://github.com/chenquincy/vue-dynamic-form-component/commit/74d63c6)) 131 | 132 | 133 | 134 | ## [1.0.1](https://github.com/chenquincy/vue-dynamic-form-component/compare/v1.0.0...v1.0.1) (2019-06-20) 135 | 136 | 137 | ### Bug Fixes 138 | 139 | * change name to vue-dynamic-form-component ([2ffea42](https://github.com/chenquincy/vue-dynamic-form-component/commit/2ffea42)) 140 | * change package name ([8b9e15c](https://github.com/chenquincy/vue-dynamic-form-component/commit/8b9e15c)) 141 | * parent form's label-width compute error. ([8d5b369](https://github.com/chenquincy/vue-dynamic-form-component/commit/8d5b369)) 142 | 143 | 144 | 145 | # 1.0.0 (2019-06-19) 146 | 147 | 148 | ### Bug Fixes 149 | 150 | * child object init error ([93c9995](https://github.com/chenquincy/vue-dynamic-form-component/commit/93c9995)) 151 | * package.json main error, change name to vue-dynamic-form2 ([f71b389](https://github.com/chenquincy/vue-dynamic-form-component/commit/f71b389)) 152 | 153 | 154 | ### Features 155 | 156 | * finish dynamic-form develop. ([839a3c0](https://github.com/chenquincy/vue-dynamic-form-component/commit/839a3c0)) 157 | * finish version 0.0.1 ([944e16a](https://github.com/chenquincy/vue-dynamic-form-component/commit/944e16a)) 158 | * finish vue-dynamic-form's data and props init. ([19b9a5e](https://github.com/chenquincy/vue-dynamic-form-component/commit/19b9a5e)) 159 | * finish vue-dynamic-input component. ([4ceb688](https://github.com/chenquincy/vue-dynamic-form-component/commit/4ceb688)) 160 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 QuincyChen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | **vue-dynamic-form-component** is a dynamic form component base on [element-ui](https://element.faas.ele.me/#/zh-CN) and [async-validator](https://github.com/yiminghe/async-validator). You just need to write **descriptors**(reference to [async-validator](https://github.com/yiminghe/async-validator)) of the data you want, **vue-dynamic-form-component** will generate the form automatically. 4 | 5 | ## Docs 6 | 7 | - [English Docs](https://vue-dynamic-form.quincychen.cn) 8 | - [中文文档](https://vue-dynamic-form.quincychen.cn/zh/) 9 | 10 | ## Usage Example 11 | 12 | ![vue-dynamic-form-component.gif](https://raw.githubusercontent.com/chenquincy/vue-dynamic-form-component/master/public/vue-dynamic-form-component.gif) 13 | 14 | ``` vue 15 | 26 | 27 | 77 | ``` 78 | 79 | ## Features 80 | 81 | - Generate form from **descriptors** 82 | - Support almost all data type 83 | - Support **multi-level form** for `Object `/ `Array` / `Hashmap` 84 | - Support data **validation** 85 | - **Multi-Languages** support 86 | - Support **custom component** 87 | 88 | ## Todo 89 | 90 | **vue-dynamic-form-component** can do more. There are a few things that it currently doesn't support but are planned: 91 | 92 | - [x] Custom component props 93 | - [x] Custom component event 94 | - [x] Custom component 95 | - [ ] Custom theme 96 | - [ ] Value change event 97 | 98 | ## Question 99 | 100 | Please submit your question in [Github Issue](https://github.com/chenquincy/vue-dynamic-form-component/issues) . 101 | 102 | ## License 103 | 104 | [MIT license](https://tldrlegal.com/license/mit-license) 105 | 106 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /dev/App.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 49 | 50 | 59 | -------------------------------------------------------------------------------- /dev/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenquincy/vue-dynamic-form-component/8d061c21d7fb637a5117d3a65ee6cc438e348c48/dev/assets/logo.png -------------------------------------------------------------------------------- /dev/descriptor.js: -------------------------------------------------------------------------------- 1 | export default { 2 | string: { type: 'string', required: true, disabled: true, placeholder: 'textarea placeholder', props: { autocomplete: 'on', type: 'textarea', rows: 4 } }, 3 | url: { type: 'url', message: 'The url must be an url' }, 4 | object: { 5 | type: 'object', 6 | label: 'object label', 7 | fields: { 8 | boolean: { type: 'boolean', required: true }, 9 | string: { type: 'string', required: true, hidden: false }, 10 | date: { type: 'date', required: true }, 11 | url: { type: 'url', message: 'The url must be an url', props: { placeholder: 'please input the url' } } 12 | } 13 | }, 14 | hashmap: { 15 | type: 'object', 16 | label: 'hashmap label', 17 | defaultField: { 18 | type: 'string', 19 | required: true 20 | } 21 | }, 22 | array: { 23 | type: 'array', 24 | label: 'array label', 25 | defaultField: { 26 | type: 'object', 27 | fields: { 28 | string: { type: 'string', required: true }, 29 | url: { type: 'url', message: 'The url must be an url', props: { placeholder: 'please input the url' } } 30 | } 31 | } 32 | }, 33 | multiSelect: { 34 | type: 'array', 35 | label: 'array label', 36 | defaultField: { 37 | type: 'enum', 38 | multiple: true, 39 | enum: [0, 1, 2, 3], 40 | options: [ 41 | { label: 'option-0', value: 0, disabled: true }, 42 | { label: 'option-1', value: 1 }, 43 | { label: 'option-2', value: 2 }, 44 | { label: 'option-3', value: 3 } 45 | ] 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /dev/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import ElementUI from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/index.css' 4 | import locale from 'element-ui/lib/locale/lang/en' 5 | import DynamicForm from '../packages/index' 6 | 7 | import App from './App.vue' 8 | 9 | Vue.config.productionTip = false 10 | Vue.use(ElementUI, { locale }) 11 | Vue.use(DynamicForm) 12 | 13 | new Vue({ 14 | render: h => h(App) 15 | }).$mount('#app') 16 | -------------------------------------------------------------------------------- /docs/.vuepress/components/array-demo.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /docs/.vuepress/components/base-data-demo.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /docs/.vuepress/components/code-demo.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 34 | 35 | -------------------------------------------------------------------------------- /docs/.vuepress/components/custom-component.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /docs/.vuepress/components/custom-message.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /docs/.vuepress/components/custom-validator.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /docs/.vuepress/components/form-demo.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | -------------------------------------------------------------------------------- /docs/.vuepress/components/form-operation.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /docs/.vuepress/components/hashmap-demo.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /docs/.vuepress/components/home-demo.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /docs/.vuepress/components/object-demo.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | head: [ 3 | ['link', { rel: 'icon', href: '/logo.png' }] 4 | ], 5 | locales: { 6 | '/': { 7 | lang: 'en-US', 8 | title: 'vue-dynamic-form-component', 9 | description: 'Vue dynamic nested form component. Support nested Object/Hashmap/Array. Can custom component yourself.' 10 | }, 11 | '/zh/': { 12 | lang: 'zh-CN', 13 | title: 'vue-dynamic-form-component', 14 | description: 'Vue动态嵌套表单组件,支持嵌套对象/Hashmap/数组, 可自定义表单组件' 15 | } 16 | }, 17 | themeConfig: { 18 | sidebarDepth: 2, 19 | lastUpdated: 'Last Updated', 20 | locales: { 21 | '/': { 22 | selectText: 'Languages', 23 | label: 'English', 24 | sidebar: [ 25 | { title: 'Guide', collapsable: false, children: ['/guide/', '/getting-started/', '/i18n/'] }, 26 | { title: 'API', collapsable: false, children: ['/api/dynamic-form/', '/api/descriptors/'] } 27 | ] 28 | }, 29 | '/zh/': { 30 | selectText: '语言', 31 | label: '简体中文', 32 | sidebar: [ 33 | { title: '指南', collapsable: false, children: ['/zh/guide/', '/zh/getting-started/', '/zh/i18n/'] }, 34 | { title: 'API', collapsable: false, children: ['/zh/api/dynamic-form/', '/zh/api/descriptors/'] } 35 | ] 36 | } 37 | }, 38 | nav: [ 39 | { text: 'GitHub', link: 'https://github.com/chenquincy/vue-dynamic-form-component' }, 40 | { text: 'v1.x', link: 'https://vue-dynamic-form.v1.quincychen.cn' } 41 | ] 42 | }, 43 | plugins: { 44 | 'sitemap': { 45 | hostname: 'https://vue-dynamic-form.quincychen.cn', 46 | exclude: ['/404.html'] 47 | }, 48 | '@vuepress/plugin-google-analytics': { 49 | ga: 'UA-145341224-1' 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /docs/.vuepress/demos/cdn.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 31 | -------------------------------------------------------------------------------- /docs/.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import ElementUI from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/index.css' 4 | import locale from 'element-ui/lib/locale/lang/en' 5 | import DynamicForm from '../../packages/index' 6 | import './public/css/index.css' 7 | 8 | export default ({ 9 | Vue 10 | }) => { 11 | Vue.use(ElementUI, { locale }) 12 | Vue.use(DynamicForm) 13 | } 14 | -------------------------------------------------------------------------------- /docs/.vuepress/public/css/index.css: -------------------------------------------------------------------------------- 1 | .el-date-picker table { 2 | display: table; 3 | } 4 | -------------------------------------------------------------------------------- /docs/.vuepress/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenquincy/vue-dynamic-form-component/8d061c21d7fb637a5117d3a65ee6cc438e348c48/docs/.vuepress/public/logo.png -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /logo.png 4 | actionText: Getting Started → 5 | actionLink: /getting-started/ 6 | meta: 7 | - name: keywords 8 | content: vue dynamic nested form component vue-dynamic-form vue-dynamic-form-component 9 | - name: google-site-verification 10 | content: -lX8u-MYYDkf5y6QhZWJqZoDxVkmAu004XwR3ynRwFg 11 | features: 12 | - title: Generate form by descriptors Or custom component 13 | details: Write only data's descriptor(extend from async-validate), then automatically generate form(use element-ui) 14 | - title: Support almost all data type with validation 15 | details: Support almost all data type and normal data validation(email, url, ... etc) 16 | - title: Support nested object/array 17 | details: Support nested object/array. Generate multi-level form, darken child form's background automatically 18 | footer: MIT Licensed | Copyright © QuincyChen 19 | --- 20 | 21 | 22 | 23 | ### Example 24 | 25 | 26 | 27 | <<< @/docs/.vuepress/components/home-demo.vue 28 | 29 | -------------------------------------------------------------------------------- /docs/api/descriptors/README.md: -------------------------------------------------------------------------------- 1 | # descriptors 2 | 3 | **descriptors** is extended from [async-validator](https://github.com/yiminghe/async-validator). Just need to write **descriptors** follow the rules,**vue-dynamic-form-component** will generate the form component automatically(use [element-ui](https://element.faas.ele.me)). 4 | 5 | **descriptors** 's format: `{ : }`, the `descriptor` can be object or array. 6 | 7 | 8 | 9 | ## descriptor 10 | 11 | ::: tip Warm Prompt 12 | **descriptor** can be `object` or `array`, but the `fields`, `defaultFields`, `label`, `hidden`, `disabled`, `options`, `component` props should be brother props with `type`. In addition, there is only one object include `type` prop in array. 13 | ::: 14 | 15 | ### type 16 | 17 | `string`, declare the type of data's field, include common data type. 18 | 19 | ::: warning 注意 20 | 21 | Note that [descriptor.component.name](/zh/api/descriptors/#props) has higher priority, the follow rules are invalid if `descriptor.component.name` is not null. 22 | ::: 23 | 24 | | Value | Description | Component | 25 | | --------- | ------------------------------------------------------------ | ------------------- | 26 | | `string` | Must be a string | `el-input` | 27 | | `number` | Must be a number | `el-input` | 28 | | `boolean` | Must be a boolean | `el-switch` | 29 | | `regexp` | Must be an instance of `RegExp` or a string that does not generate an exception when creating a new `RegExp`. | `el-input` | 30 | | `integer` | Must be of type `number` and an integer. | `el-input` | 31 | | `float` | Must be of type `number` and a floating point number. | `el-input` | 32 | | `enum` | Value must exist in the `enum`. Can be used with [enum, options](/api/descriptors/#enum-options) | `el-select` | 33 | | `date` | Value must be valid as determined by `Date` | `el-date-picker` | 34 | | `url` | Must be of type `url` | `el-input` | 35 | | `hex` | Must be of type `hex` | `el-input` | 36 | | `email` | Must be of type `email` | `el-input` | 37 | | `object` | Must be of type `object` and not `Array.isArray`. Use with `fields` or `defaultField` | `dynamic-form-item` | 38 | | `array` | Must be an array as determined by `Array.isArray`. Use with `defaultField` | `dynamic-form-item` | 39 | 40 | ### label 41 | 42 | `string`, field's label in form, should be declared with `type` in the same object. 43 | 44 | ### hidden 45 | 46 | `boolean` , whether hidden the input component of value, should be declared with `type` in the same object. Note that the hidden value still will be validated while validating. 47 | 48 | ### disabled 49 | 50 | `boolean`, whether input component disabled, default `false`. 51 | 52 | ### required 53 | 54 | `boolean`, is field required, without or `false` means the field is not required. 55 | 56 | ### pattern 57 | 58 | `Regexp`, the field's value must match to pass validation. 59 | 60 | ### min | max 61 | 62 | `number`, can be used for `string`, `array`, `number` 63 | 64 | For `string` and `array`, field's length should in `[min, max]`. For `number`, field's value should in `[min, max]`. 65 | 66 | ### len 67 | 68 | `number`, can be used for `string`, `array`, `number` 69 | 70 | For `string` and `array`, field's length should equal to `len`. For `number`, field's value should equal to `len`. 71 | 72 | > If the `len` property is combined with the `min` and `max` range properties, `len` takes precedence. 73 | 74 | ### enum, options 75 | 76 | `array`, only can be used while `type === 'enum'`, here is the format: 77 | 78 | ```js 79 | { 80 | type: 'enum', 81 | enum: [0, 1, 2], 82 | options: [ 83 | { label: 'female', value: 0 }, 84 | { label: 'male', value: 1 }, 85 | { label: 'secret', value: 2, disabled: true } 86 | ] 87 | } 88 | ``` 89 | 90 | `options` is optional, `label === value` without `options` 91 | 92 | ### whitespace 93 | 94 | `boolean`, only can be used while `type === 'string'`. Validate error if value only contain whitespace. 95 | 96 | ### message 97 | 98 | `string`, cover the default error message, one validate rule can have one message. 99 | 100 | ```js 101 | const descriptors = { 102 | name: [ 103 | { type: 'string', required: true, message: 'username is required' }, 104 | { min: 3, max 20, message: 'username length must between 3 to 20' }, 105 | { whitespace: true, message: 'username can not be whitespace' } 106 | ] 107 | } 108 | ``` 109 | 110 | ### fields 111 | 112 | `object`, only can be used while `type === 'object'`. You can use it if the child fields have different value format. Actually, it is the `descriptors` of field's value, your use it to get **nested object**. 113 | 114 | ```js 115 | const descriptors = { 116 | company: { 117 | type: 'object', 118 | fields: { 119 | name: { type: 'string', required: true }, 120 | address: { 121 | type: 'object', 122 | fields: { 123 | country: { type: 'string', required: true }, 124 | province: { type: 'string', required: true } 125 | } 126 | } 127 | } 128 | } 129 | } 130 | ``` 131 | 132 | The above `descriptors` get the data with this structure: 133 | 134 | ```js 135 | { 136 | company: { 137 | name: String, 138 | address: { 139 | country: String, 140 | province: String 141 | } 142 | } 143 | } 144 | ``` 145 | 146 | ### defaultField 147 | 148 | `object | array`, priority to `fields`, only can be used while `type === 'object' | 'array'`. You can use it if the child fields have same value format. Actually, it is the `descriptor` of field, you can use it to get complex data structures like **Hashmap** or **multidimensional arrays**. 149 | 150 | ```js 151 | const descriptors = { 152 | dict: { 153 | type: 'object', 154 | defaultField: { type: 'string', required: true } 155 | }, 156 | array2d: { 157 | type: 'array', 158 | defaultField: { 159 | type: 'array', 160 | defaultField: { type: 'string', required: true } 161 | } 162 | } 163 | } 164 | ``` 165 | 166 | The above `descriptors` get the data with this structure: 167 | 168 | ```js 169 | { 170 | dict: { 171 | : String 172 | }, 173 | array2d: [ 174 | [String] 175 | ] 176 | } 177 | ``` 178 | 179 | ### validator 180 | 181 | `Function`, custom the validate function with format: `validator(rule: Object, value, callback: Function)` 182 | 183 | - `rule` 184 | 185 | the validate rule of the field 186 | 187 | - `value` 188 | 189 | the value of the field 190 | 191 | - `callback` 192 | 193 | callback function of validation. `callback(new Error())` means validate error, `message` is the error message. `callback()` means validate success. 194 | 195 | ```js 196 | const descriptors = { 197 | name: [ 198 | { type: 'string', required: true }, 199 | { 200 | validator: function (rule, value, callback) { 201 | if (value.length < 5) { 202 | return callback(new Error('name should logger than 5')) 203 | } 204 | if (value.indexOf('/%$') !== -1) { 205 | return callback(new Error('name can not include /%$')) 206 | } 207 | return callback() 208 | } 209 | } 210 | ] 211 | } 212 | ``` 213 | 214 | ### component 215 | 216 | Provided for user to custom the component easily, it can custom the component's content, props and events. 217 | 218 | ``` js 219 | // component demo 220 | { 221 | name: 'el-button', 222 | props: { 223 | type: 'primary', 224 | size: 'small' 225 | }, 226 | events: { 227 | click () { 228 | console.log('button click') 229 | } 230 | } 231 | } 232 | ``` 233 | 234 | #### name 235 | 236 | `string` type, custom to use which element or component. 237 | 238 | #### props 239 | 240 | `object` type, it will be the value of input component's `v-bind` prop(input component refer to [descriptor.type](/api/descriptors/#type)). You can custom the component props with this option, there are some common props like: `placeholder`, `disabled`, ... , etc. Other props of component refer to [element-ui](https://element.faas.ele.me/#/en-US/component/installation). Note that it will be the value of the custom component's `v-bind` prop, if `component.name` is not null. 241 | 242 | #### events 243 | 244 | `object` type, it will be the value of input component's `v-on` prop(input component refer to [descriptor.type](/api/descriptors/#type)). You can custom the component props with this option, there are some common prop like: `placeholder`, `disabled`, ... , etc. Other props of component refer to [element-ui](https://element.faas.ele.me/#/en-US/component/installation). Note that it will be the value of the custom component's `v-on` prop, if `component.name` is not null. 245 | 246 | #### children 247 | 248 | `children` is used to custom the content of component. It has two types: 249 | 250 | - `string` : the content is plain text. It will be inserted with `span` element 251 | - `array[component | string]` : the content has more than one element, the array item has two types: 252 | - `component` : same structure with [component](/api/descriptors/#component) 253 | - `string` : the item is plain text. It will be inserted with `span` element -------------------------------------------------------------------------------- /docs/api/dynamic-form/README.md: -------------------------------------------------------------------------------- 1 | # dynamic-form 2 | 3 | ## Props 4 | 5 | | prop | description | type | option values | default | required | 6 | | --------------- | ------------------------------------------------------------ | ------- | ------------------- | ------- | -------- | 7 | | value | form data | object | | | yes | 8 | | lang | language | string | en_US,zh_CN | zh_CN | | 9 | | languages | custom language pack | object | | | | 10 | | descriptors | descriptors of form data, refer to [descriptor](/zh/api/descriptors/) | object | | | yes | 11 | | size | size of form component | string | medium,small,mini | small | | 12 | | disabled | whether form is disabled | boolean | | false | | 13 | | backgroundColor | root form background color | string | Hex color or 'none' | none | | 14 | | fontSize | font size of form | number | | 14 | | 15 | | bgColorOffset | form background color offset | number | | 8 | | 16 | | showOuterError | whether show parent component's error | boolean | | true | | 17 | 18 | `languages` format: 19 | 20 | ```js 21 | // refer to lang prop 22 | { 23 | : { 24 | addKeyPlaceholder: 'Input the key you want to add', 25 | addKeyButtonText: 'Add Key', 26 | addArrayItemButtonText: 'Add Item' 27 | } 28 | } 29 | ``` 30 | 31 | 32 | 33 | ## Slots 34 | 35 | | slot name | description | 36 | | ---------- | ------------------------------------------------------------ | 37 | | operations | operation slot for form, use to put submit/reset button generally | 38 | 39 | ## Methods 40 | 41 | | method | description | params | 42 | | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | 43 | | validate | validate the form. Takes a optional callback function as a param. Callback function will be executed with one param(validate result) while having the callback param. Return a promise(resolve validate result) without callback param. | Function(callback: (valid) => {}):void \| Function():Promise(Boolean) | 44 | | resetFields | reset form and remove validation result | () => {} | 45 | | clearValidate | clear validation message of form | () => {} | 46 | 47 | ## Example 48 | 49 | 50 | 51 | <<< @/docs/.vuepress/components/form-demo.vue 52 | 53 | -------------------------------------------------------------------------------- /docs/getting-started/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ::: tip Warm Prompt 4 | vue-dynamic-form-component should use with [Vue](https://vuejs.org/) and [element-ui](https://element.faas.ele.me), please install them first. 5 | ::: 6 | 7 | ## Installation 8 | 9 | ### CDN 10 | 11 | Get the latest version from [unpkg.com/vue-dynamic-form-component](https://unpkg.com/vue-dynamic-form-component/), and import the javascript file in your page. 12 | 13 | <<<@/docs/.vuepress/demos/cdn.html 14 | 15 | 16 | 17 | ### NPM 18 | 19 | Installing with npm is recommended and it works seamlessly with [webpack](https://webpack.js.org/). 20 | 21 | ``` bash 22 | # yarn 23 | yarn add vue-dynamic-form-component 24 | # or npm 25 | npm install vue-dynamic-form-component 26 | ``` 27 | 28 | 29 | 30 | ## Registration 31 | 32 | ### Global Registered 33 | 34 | ``` js 35 | import Vue from 'Vue' 36 | import ElementUI from 'element-ui' 37 | import 'element-ui/lib/theme-chalk/index.css' 38 | Vue.use(ElementUI) 39 | 40 | import DynamicForm from 'vue-dynamic-form-component' 41 | Vue.use(DynamicForm) 42 | ``` 43 | 44 | ### Registered in Component 45 | 46 | ::: tip Warm Prompt 47 | Don't forget to register the [element-ui](https://element.faas.ele.me) first. 48 | ::: 49 | 50 | ``` vue 51 | 59 | ``` 60 | 61 | ## Usage 62 | 63 | ### Simple Data Type 64 | 65 | The component support common data type:`string`, `number`, `boolean`, `integer`, `float`, `enum`, `date`, `url`, `hex`, `email` and so on, more type reference to [descriptor.type](/api/descriptors/#type) 66 | 67 | 68 | 69 | <<<@/docs/.vuepress/components/base-data-demo.vue 70 | 71 | ### Object 72 | 73 | To generate **Object**, use `type: 'object'` with `fields` 74 | 75 | 76 | 77 | <<<@/docs/.vuepress/components/object-demo.vue 78 | 79 | ### Hashmap 80 | 81 | To generate **Hashmap**, use `type: 'object'` with `defaultField` 82 | 83 | 84 | 85 | <<<@/docs/.vuepress/components/hashmap-demo.vue 86 | 87 | ### Array 88 | 89 | To generate **Array**, use `type: 'array'` with `defaultField`. If the array items are enumerable, you can use `type: 'enum'` in `defaultField` with `multiple: true`. It will generate a multi-select component. 90 | 91 | 92 | 93 | <<<@/docs/.vuepress/components/array-demo.vue 94 | 95 | ### Custom Validation 96 | 97 | Generally, you don't need to write extra code for field's validation. **vue-dynamic-form-component** provides default validate rule and message base on `type`。 98 | 99 | If you need to custom the validate rule or message, you can use the following methods 100 | 101 | #### Custom Validate Message 102 | 103 | You can cover the default validate message by adding the `message` prop. You can add multi rule for one field by using `Array`. 104 | 105 | ::: warning Warning 106 | Special prop `label`, `fields`, `defaultField` , ...etc should be used with `type` in one rule. Reference to [descriptor.type](/api/descriptors/#descriptor) 107 | ::: 108 | 109 | 110 | 111 | <<<@/docs/.vuepress/components/custom-message.vue 112 | 113 | #### Custom Validator 114 | 115 | **vue-dynamic-form-component** provides many configurations to meet most of validate rules. Here are some common configurations, more configurations reference to [descriptor](/api/descriptors/#descriptor). 116 | 117 | | Prop | Type | Description | 118 | | --------- | ------------------------------- | ------------------------------------------------------------ | 119 | | required | Boolean | whether the prop value is required | 120 | | pattern | RegExp | regexp to match prop value | 121 | | len | Number | validate `length` of `string` or `array` | 122 | | validator | Function(rule, value, callback) | Custom validate function, `callback(Error)` mean validate error, `callback()` mean validate success | 123 | 124 | 125 | 126 | <<<@/docs/.vuepress/components/custom-validator.vue 127 | 128 | ### Custom Component 129 | 130 | ::: tip Warm Prompt 131 | 132 | To custom component, you should ensure `version >= 2.5.0` 133 | ::: 134 | 135 | **vue-dynamic-form-component** has components for common data type inside, so you don't need to custom the component generally. But sometimes, we need to custom the component, such as upload a file. To meet these scenes, I provide a way to custom the component. Usage like this: 136 | 137 | 138 | 139 | <<<@/docs/.vuepress/components/custom-component.vue 140 | 141 | The component was writed by used `v-model` , so you can use the custom component directly after changing the `component.name`. The component structure in descriptors is: 142 | 143 | ``` js 144 | component: { 145 | name: 'el-radio-group', // component's name 146 | props: {}, // component's props 147 | events: {}, // component's events 148 | children: [], // component or string(plain text dom) 149 | } 150 | ``` 151 | 152 | #### Component without v-model 153 | 154 | If you want to use component like `el-image` that without v-model, you need to write a component yourself. 155 | 156 | First, write the custom component: 157 | 158 | ``` vue 159 | 164 | 165 | 185 | ``` 186 | 187 | Then, register it global: 188 | 189 | ``` js 190 | import MyImage from './MyImage' 191 | Vue.component(MyImage.name, MyImage) 192 | ``` 193 | 194 | Finally, you can use the component like previous step. 195 | 196 | Also, you can add more feature to you custom component, such as set to default value when the image loaded error: 197 | 198 | ``` vue 199 | 204 | 205 | 230 | ``` 231 | 232 | 233 | 234 | ### Form Operations 235 | 236 | **vue-dynamic-form-component** provides some common methods to operate form, you can use them with `operations` slot. 237 | 238 | ::: tip Warm Prompt 239 | The document import the `el-button` component for convenience, you should import by yourself, if you need it in actual use. You can refer the specific parameters of form methods to [Component Methods](/api/dynamic-form/#methods). 240 | ::: 241 | 242 | 243 | 244 | <<<@/docs/.vuepress/components/form-operation.vue{6,7,8,9,10} 245 | 246 | -------------------------------------------------------------------------------- /docs/guide/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | **vue-dynamic-form-component** is a dynamic form component base on [element-ui](https://element.faas.ele.me/#/zh-CN) and [async-validator](https://github.com/yiminghe/async-validator) , support nested Object/Hashmap/Array. You just need to write **descriptors**(reference to [async-validator](https://github.com/yiminghe/async-validator)) of the data you want, **vue-dynamic-form-component** will generate the form automatically. 4 | 5 | ## Features 6 | 7 | - Generate form from **descriptors** 8 | - Support almost all data type 9 | - Support **nested form** for `Object `/ `Array` / `Hashmap` 10 | - Support data **validation** 11 | - **Multi-Languages** support 12 | - Support **custom component** 13 | 14 | ## Question 15 | 16 | Please submit your question in [Github Issue](https://github.com/chenquincy/vue-dynamic-form-component/issues) . 17 | 18 | ## License 19 | 20 | [MIT license](https://tldrlegal.com/license/mit-license) 21 | 22 | -------------------------------------------------------------------------------- /docs/i18n/README.md: -------------------------------------------------------------------------------- 1 | ## Internationalization 2 | 3 | The default language of component is Chinese. If you want to use the another language, you need to do some configurations. 4 | 5 | ## element-ui setting 6 | 7 | Firstly, we should set the i18n configurations of `element-ui`, because vue-dynamic-form-component is based on `element-ui`. 8 | 9 | ``` js 10 | import Vue from 'vue' 11 | import ElementUI from 'element-ui' 12 | import locale from 'element-ui/lib/locale/lang/en' 13 | 14 | Vue.use(ElementUI, { locale }) 15 | ``` 16 | 17 | You can see the more configuration way here: [element-ui multi-languages setting](https://element.faas.ele.me/#/zh-CN/component/i18n) 18 | 19 | ## component setting 20 | 21 | The component is designed with chinese and english languages inside, you can toggle language by `lang` prop. 22 | 23 | ``` vue 24 | 31 | ``` 32 | 33 | prop-value map is: 34 | 35 | | Language | Value | 36 | | -------- | ----- | 37 | | Chinese | zh_CN | 38 | | English | en_US | 39 | 40 | ### Custom language packages 41 | 42 | You can use `languages` prop to custom the languages by yourself, this is the `languages` format: 43 | 44 | ``` js 45 | { 46 | 'en_US': { 47 | addKeyPlaceholder: 'Input the key you want to add', 48 | addKeyButtonText: 'Add Key', 49 | addArrayItemButtonText: 'Add Item' 50 | }, 51 | 'zh_CN': { 52 | ... 53 | } 54 | } 55 | ``` 56 | 57 | ``` html 58 | 66 | ``` 67 | 68 | -------------------------------------------------------------------------------- /docs/zh/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /logo.png 4 | actionText: 快速开始 → 5 | actionLink: /zh/getting-started/ 6 | meta: 7 | - name: keywords 8 | content: vue 动态 嵌套 表单 多级 组件 vue-dynamic-form vue-dynamic-form-component 9 | features: 10 | - title: 使用descriptors生成表单或自定义组件 11 | details: 只需要提供目标数据的descriptor(基于async-validator扩展),就可以自动生成对应的表单元素(基于element-ui) 12 | - title: 支持绝大部分数据类型及其校验 13 | details: 支持绝大部分数据类型、常见类型校验(如:email、url等) 14 | - title: 支持多级数据对象 15 | details: 支持多级对象、数组,自动生成多级嵌套表单,子表单背景自定义加深 16 | footer: MIT Licensed | Copyright © QuincyChen 17 | --- 18 | 19 | 20 | 21 | ### 示例 22 | 23 | 24 | 25 | <<< @/docs/.vuepress/components/home-demo.vue 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/zh/api/descriptors/README.md: -------------------------------------------------------------------------------- 1 | # descriptors 2 | 3 | **descriptors** 基于 [async-validator](https://github.com/yiminghe/async-validator) 扩展。只需要按照规则编写 **descriptors**,**vue-dynamic-form-component** 就会自动生成对应的表单元素(表单组件基于 [element-ui](https://element.faas.ele.me))。 4 | 5 | **descriptors** 的格式为:`{ : }`,其中,**descriptor** 可以是对象或者数组。 6 | 7 | ## descriptor 8 | 9 | ::: warning 注意 10 | **descriptor** 可以是对象或者数组,但是,当 **descriptor** 为数组时,与数据类型定义有关的属性( `fields`、`defaultFields`)和扩展属性(`label`、`hidden`、`disabled`、`options`、`component`) 需和 `type` 属性在同一对象中,并且数组中有且仅有一个包含 `type` 属性的对象。 11 | ::: 12 | 13 | ### type 14 | 15 | `string` 类型,表示字段类型,包含常见的数据类型 16 | 17 | ::: warning 注意 18 | [descriptor.component.name](/zh/api/descriptors/#props) 优先级更高,当 `descriptor.component.name` 不为空时,以下规则不生效 19 | ::: 20 | 21 | | 值 | 说明 | 对应组件 | 22 | | --------- | ------------------------------------------------------------ | ------------------- | 23 | | `string` | 字符串类型 | `el-input` | 24 | | `number` | `number`类型,自动添加 `.number` 修饰符 | `el-input` | 25 | | `boolean` | 布尔类型 | `el-switch` | 26 | | `regexp` | 正则表达式,必须是可以正确转化为 `RegExp` 实例的字符串 | `el-input` | 27 | | `integer` | `number` 类型的整数,自动添加 `.number` 修饰符 | `el-input` | 28 | | `float` | `number` 类型的浮点数,自动添加 `.number` 修饰符 | `el-input` | 29 | | `enum` | 枚举类型,需要和 [enum, options](/zh/api/descriptors/#enum-options) 属性配合使用,值必须是 `enum` 数组中的一个 | `el-select` | 30 | | `date` | 必须是合法的 `Date` 对象 | `el-date-picker` | 31 | | `url` | 符合链接格式的字符串 | `el-input` | 32 | | `hex` | 符合哈希格式的字符串 | `el-input` | 33 | | `email` | 符合邮件格式的字符串 | `el-input` | 34 | | `object` | `object` 类型,配合 `fields` 和 `defaultField` 使用 | `dynamic-form-item` | 35 | | `array` | `array` 类型,配合 `defaultField` 使用 | `dynamic-form-item` | 36 | 37 | ### label 38 | 39 | `string` 类型,表单中对应字段的label值,需和 `type` 在同一个对象中 40 | 41 | ### hidden 42 | 43 | `boolean` 类型,是否隐藏对应的输入组件,需和 `type` 在同一个对象中。需要注意的是,触发校验时,被隐藏的组件值依然会进行校验。 44 | 45 | ### disabled 46 | 47 | `boolean` 类型,输入组件是否不可编辑,默认为 `false` 48 | 49 | ### required 50 | 51 | `boolean` 类型,表示字段值是否为必须值,不填表示非必须 52 | 53 | ### pattern 54 | 55 | `Regexp` 类型,字段值必须符合该正则表达式 56 | 57 | ### min | max 58 | 59 | `number` 类型,适用于 `string`, `array`, `number` 类型 60 | 61 | 对于 `string` 或 `array` ,字段值的 `length` 必须介于 `min` 和 `max` 之间,对于 `number` 类型,值必须介于 `min` 和 `max` 之间 62 | 63 | ### len 64 | 65 | 精准匹配字段值的长度或大小,规则类似 `min | max`,`len` 优先级高于 `min | max` 66 | 67 | ### enum, options 68 | 69 | `array`类型,仅当 `type` 为 `enum` 时有效,用于列举字段值的所有选项,格式: 70 | 71 | ``` js 72 | { 73 | type: 'enum', 74 | enum: [0, 1, 2], 75 | options: [ 76 | { label: 'female', value: 0 }, 77 | { label: 'male', value: 1 }, 78 | { label: 'secret', value: 2, disabled: true } 79 | ] 80 | } 81 | ``` 82 | 83 | 当没有 `options` 时,`label === value` 84 | 85 | ### whitespace 86 | 87 | `boolean` 类型,仅适用于 `type === 'string'`,判断字段值是否为空(只包含空格的值也视为空) 88 | 89 | ### message 90 | 91 | `string` 类型,用于覆盖默认的错误提示信息,一个验证规则对应一条错误提示。 92 | 93 | ``` js 94 | const descriptors = { 95 | name: [ 96 | { type: 'string', required: true, message: 'username is required' }, 97 | { min: 3, max 20, message: 'username length must between 3 to 20' }, 98 | { whitespace: true, message: 'username can not be whitespace' } 99 | ] 100 | } 101 | ``` 102 | 103 | ### fields 104 | 105 | `object` 类型,只在 `type === 'object'` 时有效,当子字段值是不同格式时使用。事实上它就是字段值的 `descriptors`,你可以使用它来获取 **嵌套对象**。 106 | 107 | ``` js 108 | const descriptors = { 109 | company: { 110 | type: 'object', 111 | fields: { 112 | name: { type: 'string', required: true }, 113 | address: { 114 | type: 'object', 115 | fields: { 116 | country: { type: 'string', required: true }, 117 | province: { type: 'string', required: true } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | ``` 124 | 125 | 以上 `descriptors` 获取的数据格式为 126 | 127 | ``` js 128 | { 129 | company: { 130 | name: String, 131 | address: { 132 | country: String, 133 | province: String 134 | } 135 | } 136 | } 137 | ``` 138 | 139 | ### defaultField 140 | 141 | `object | array` 类型,优先级高于 `fields`,只在 `type === 'object' | 'array'` 时有效,当子字段值是相同格式时使用。事实上它就是字段值的 `descriptor`,你可以使用它来获取 **Hashmap** 或 **多维数组** 等复杂类型。 142 | 143 | ``` js 144 | const descriptors = { 145 | dict: { 146 | type: 'object', 147 | defaultField: { type: 'string', required: true } 148 | }, 149 | array2d: { 150 | type: 'array', 151 | defaultField: { 152 | type: 'array', 153 | defaultField: { type: 'string', required: true } 154 | } 155 | } 156 | } 157 | ``` 158 | 159 | 以上 `descriptor` 获取的数据格式为 160 | 161 | ``` js 162 | { 163 | dict: { 164 | : String 165 | }, 166 | array2d: [ 167 | [String] 168 | ] 169 | } 170 | ``` 171 | 172 | ### validator 173 | 174 | `Function` 类型,自定义校验函数,可以通过它来自定义校验规则,格式为:`validator(rule: Object, value, callback: Function)` 175 | 176 | * `rule` 177 | 178 | 当前校验的规则对象 179 | 180 | * `value` 181 | 182 | 对应字段的当前值 183 | 184 | * `callback` 185 | 186 | 校验回调函数,`callback(new Error())` 时表示校验不通过,`message`为显示的错误提示,`callback()` 表示校验通过 187 | 188 | ``` js 189 | const descriptors = { 190 | name: [ 191 | { type: 'string', required: true }, 192 | { 193 | validator: function (rule, value, callback) { 194 | if (value.length < 5) { 195 | return callback(new Error('name should logger than 5')) 196 | } 197 | if (value.indexOf('/%$') !== -1) { 198 | return callback(new Error('name can not include /%$')) 199 | } 200 | return callback() 201 | } 202 | } 203 | ] 204 | } 205 | ``` 206 | 207 | ### component 208 | 209 | 为了方便使用者自定义组件提供的选项,可以自定义组件内容、属性和事件。 210 | 211 | ``` js 212 | // component 示例 213 | { 214 | name: 'el-button', 215 | props: { 216 | type: 'primary', 217 | size: 'small' 218 | }, 219 | events: { 220 | click () { 221 | console.log('button click') 222 | } 223 | } 224 | } 225 | ``` 226 | 227 | #### name 228 | 229 | `string` 类型,自定义组件名或元素名 230 | 231 | #### props 232 | 233 | `object` 类型,当 `component.name` 为空时,通过 `v-bind` 绑定到对应的组件(对应组件查阅 [descriptor.type](/zh/api/descriptors/#type)),可以通过该选项进行属性自定义,常见的属性有:`placeholder`、`disabled` 等,其他对应组件属性请查阅 [element-ui](https://element.faas.ele.me/#/zh-CN/component/installation) 对应组件文档;当 `component.name` 不为空时,通过 `v-bind` 绑定到声明的组件上。 234 | 235 | #### events 236 | 237 | `object` 类型,当 `component.name` 为空时,通过 `v-on` 绑定到对应的组件(对应组件查阅 [descriptor.type](/zh/api/descriptors/#type)),可以通过该选项进行事件自定义,例如:`click`、`focus` 等,其他对应组件事件请查阅 [element-ui](https://element.faas.ele.me/#/zh-CN/component/installation) 对应组件文档;当 `component.name` 不为空时,通过 `v-on` 绑定到声明的组件上。 238 | 239 | #### children 240 | 241 | `children` 用于自定义组件内容,它有两种类型: 242 | 243 | - `string` :表示内容为纯文本,使用 `span` 标签插入 244 | - `array[component | string]` : 表示组件内容有多个元素,数组元素有两种类型: 245 | - `component` : 结构和 [component](/zh/api/descriptors/#component) 相同 246 | - `string` : 表示纯文本元素,使用 `span` 标签插入 -------------------------------------------------------------------------------- /docs/zh/api/dynamic-form/README.md: -------------------------------------------------------------------------------- 1 | # dynamic-form 2 | 3 | ## 属性 4 | 5 | | 参数 | 说明 | 类型 | 可选值 | 默认值 | 必传 | 6 | | --------------- | ------------------------------------------------------------ | ------- | ----------------- | ------ | ---- | 7 | | value | 表单数据对象 | object | | | 是 | 8 | | lang | 显示语言 | string | en_US,zh_CN | zh_CN | | 9 | | languages | 自定义语言包 | object | | | | 10 | | descriptors | 表单数据对象的描述器,详见 [descriptor](/zh/api/descriptors/) | object | | | 是 | 11 | | size | 表单组件元素的大小 | string | medium,small,mini | small | | 12 | | disabled | 表单是否不可编辑 | boolean | | false | | 13 | | backgroundColor | 表单最外层背景 | string | 如:#FFFFFF | none | | 14 | | fontSize | 表单字体大小 | number | | 14 | | 15 | | bgColorOffset | 不同层级表单的背景色偏移量 | number | | 8 | | 16 | | showOuterError | 是否显示父组件的错误信息 | boolean | | true | | 17 | 18 | `languages` 格式: 19 | 20 | ``` js 21 | // 与 lang 属性对应 22 | { 23 | : { 24 | addKeyPlaceholder: 'Input the key you want to add', 25 | addKeyButtonText: 'Add Key', 26 | addArrayItemButtonText: 'Add Item' 27 | } 28 | } 29 | ``` 30 | 31 | 32 | 33 | ## 插槽 34 | 35 | | 插槽名 | 说明 | 36 | | ---------- | ------------------------------------------ | 37 | | operations | 表单操作区插槽,一般用于放置提交、重置按钮 | 38 | 39 | ## 方法 40 | 41 | | 方法 | 说明 | 参数 | 42 | | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | 43 | | validate | 触发表单的校验函数,有一个可选的callback函数。当传入callback函数时,回调函数会在校验结束后被调用;若不传参,则返回一个Promise,结果为表示校验是否通过的布尔值 | Function(callback: (valid) => {}):void \| Function():Promise(Boolean) | 44 | | resetFields | 重置表单,将所有字段重置为初始值并移除校验结果 | () => {} | 45 | | clearValidate | 移除校验结果 | () => {} | 46 | 47 | ## 示例 48 | 49 | 50 | 51 | <<< @/docs/.vuepress/components/form-demo.vue 52 | 53 | -------------------------------------------------------------------------------- /docs/zh/getting-started/README.md: -------------------------------------------------------------------------------- 1 | # 快速上手 2 | 3 | ::: tip 温馨提示 4 | vue-dynamic-form-component 依赖于 [Vue](https://vuejs.org/) 和 [element-ui](https://element.faas.ele.me),请提前安装 5 | ::: 6 | 7 | ## 安装 8 | 9 | ### CDN 10 | 11 | 目前可以通过 [unpkg.com/vue-dynamic-form-component](https://unpkg.com/vue-dynamic-form-component/) 获取最新版本的资源,使用示例: 12 | 13 | <<<@/docs/.vuepress/demos/cdn.html 14 | 15 | 16 | 17 | ### NPM 18 | 19 | 推荐使用 npm 的方式安装,它能更好地和 [webpack](https://webpack.js.org/) 打包工具配合使用。 20 | 21 | ``` bash 22 | # yarn 23 | yarn add vue-dynamic-form-component 24 | # or npm 25 | npm install vue-dynamic-form-component 26 | ``` 27 | 28 | 29 | 30 | ## 注册 31 | 32 | ### 全局注册 33 | 34 | ``` js 35 | import Vue from 'Vue' 36 | import ElementUI from 'element-ui' 37 | import 'element-ui/lib/theme-chalk/index.css' 38 | Vue.use(ElementUI) 39 | 40 | import DynamicForm from 'vue-dynamic-form-component' 41 | Vue.use(DynamicForm) 42 | ``` 43 | 44 | ### 组件内注册 45 | 46 | ::: tip 温馨提示 47 | 不要忘记提前注册 [element-ui](https://element.faas.ele.me) 48 | ::: 49 | 50 | ``` vue 51 | 59 | ``` 60 | 61 | ## 使用 62 | 63 | ### 简单类型 64 | 65 | 组件支持常见的输入类型:`string`, `number`, `boolean`, `integer`, `float`, `enum`, `date`, `url`, `email` 等等,更多类型请查阅 [descriptor.type](/zh/api/descriptors/#type) 66 | 67 | 68 | 69 | <<<@/docs/.vuepress/components/base-data-demo.vue 70 | 71 | ### 对象 72 | 73 | `type: 'object'` 与 `fields` 配合使用即可生成嵌套对象 74 | 75 | 76 | 77 | <<<@/docs/.vuepress/components/object-demo.vue 78 | 79 | ### HashMap 80 | 81 | `type: 'object'` 与 `defaultField` 配合使用即可生成 **Hashmap** 82 | 83 | 84 | 85 | <<<@/docs/.vuepress/components/hashmap-demo.vue 86 | 87 | ### 数组 88 | 89 | `type: 'array'` 与 `defaultField` 配合使用可以生成对应数组,当数组元素是可枚举类型时,推荐在 `defaultField` 中使用 `enum` 类型并设置 `multiple: true`,组件将会为这个数组生成一个多选下拉框。 90 | 91 | 92 | 93 | <<<@/docs/.vuepress/components/array-demo.vue 94 | 95 | ### 自定义校验 96 | 97 | 一般情况下,在你声明了 `type` 后,你不需要对字段编写额外的校验代码,**vue-dynamic-form-component** 自带了默认的校验规则及校验信息。 98 | 99 | 如果你需要自定义校验规则或校验信息,可以采用以下方式 100 | 101 | #### 自定义校验信息 102 | 103 | 给对应规则添加 `message` 字段即可覆盖原校验信息,可以使用数组形式给一个属性添加多条规则 104 | 105 | ::: warning 注意 106 | 特殊属性 `label`, `fields`, `defaultField` 等只能和 `type` 属性在同一规则中,具体查阅 [descriptor.type](/zh/api/descriptors/#type) 107 | ::: 108 | 109 | 110 | 111 | <<<@/docs/.vuepress/components/custom-message.vue 112 | 113 | #### 自定义规则 114 | 115 | **vue-dynamic-form-component** 提供了很多配置属性来满足大部分验证规则,以下为一些常用配置,更多配置请查阅 [descriptor](/zh/api/descriptors/#descriptor) 116 | 117 | | 属性 | 类型 | 说明 | 118 | | --------- | ------------------------------- | ------------------------------------------------------------ | 119 | | required | Boolean | 字段是否为必填 | 120 | | pattern | RegExp | 用于匹配字段值的正则表达式 | 121 | | len | Number | 验证字符串或数组的长度 | 122 | | validator | Function(rule, value, callback) | 自定义校验函数, callback(Error)表示校验失败, callback()校验通过 | 123 | 124 | 125 | 126 | <<<@/docs/.vuepress/components/custom-validator.vue 127 | 128 | ### 自定义组件 129 | 130 | ::: tip 温馨提示 131 | 使用自定义组件功能,需保证 `组件版本 >= 2.5.0` 132 | ::: 133 | 134 | **vue-dynamic-form-component** 内置了常见数据类型的组件,一般情况下你不需要自定义组件。但有些情况下我们确实需要自定义组件,为了满足特殊场景,提供了自定义组件的方式,例如,当我们想要使用单选框来代替下拉选择时,使用方式如下: 135 | 136 | 137 | 138 | <<<@/docs/.vuepress/components/custom-component.vue 139 | 140 | 组件内部使用 v-model 进行数据绑定,因此,当自定义组件可以使用 v-model ,则不需要进行特殊处理,直接更改组件名即可,descriptors 中的自定义组件格式: 141 | 142 | ``` json 143 | component: { 144 | name: 'el-radio-group', // 要使用的自定义组件名称 145 | props: {}, // 组件属性 146 | events: {}, // 绑定事件 147 | children: [], // 和组件相同的格式或者字符串表示纯文本节点 148 | } 149 | ``` 150 | 151 | #### 非 v-model 组件 152 | 153 | 如果想要使用 el-image 这类非 v-model 组件,则需要自行封装一个组件来实现。新建一个自定义组件: 154 | 155 | ``` vue 156 | 161 | 162 | 182 | ``` 183 | 184 | 全局注册该组件 185 | 186 | ``` js 187 | import MyImage from './MyImage' 188 | Vue.component(MyImage.name, MyImage) 189 | ``` 190 | 191 | 之后就可以像上一步中一样使用该组件。 192 | 193 | 当然你也可以在自定义组件中添加更多的逻辑,例如图片加载失败时替换为默认值: 194 | 195 | ``` vue 196 | 201 | 202 | 227 | ``` 228 | 229 | ### 表单操作 230 | 231 | **vue-dynamic-form-component** 提供了几个常用的方法来操作表单,可以配合 `operations` 插槽使用。 232 | 233 | ::: tip 温馨提示 234 | 为方便演示,文档中引入了 `el-button` 组件,如实际使用中需要,请自行引入。表单方法的具体参数请查阅 [组件方法](/zh/api/dynamic-form/#方法)。 235 | ::: 236 | 237 | 238 | 239 | <<<@/docs/.vuepress/components/form-operation.vue{6,7,8,9,10} 240 | 241 | -------------------------------------------------------------------------------- /docs/zh/guide/README.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | **vue-dynamic-form-component** 是一个基于 [element-ui](https://element.faas.ele.me/#/zh-CN) 和 [async-validator](https://github.com/yiminghe/async-validator) 实现的动态表单组件,支持嵌套Object/Hashmap/Array。只需要提供数据的描述器(参考[async-validator](https://github.com/yiminghe/async-validator)的descriptor),**vue-dynamic-form-component**就会自动生成对应的表单元素。 4 | 5 | 6 | 7 | ## 功能 8 | 9 | - 使用**descriptors**生成表单 10 | - 支持绝大部分数据类型 11 | - 支持**嵌套表单**,满足 `Object` / `Array` / `HashMap` 等复杂数据类型 12 | - 支持数据**校验** 13 | - **多语言**支持 14 | - 支持**自定义组件** 15 | 16 | 17 | 18 | ## 意见反馈 19 | 20 | 请通过 [Github Issue](https://github.com/chenquincy/vue-dynamic-form-component/issues) 提交您的意见反馈 21 | 22 | 23 | 24 | ## 开源证书 25 | 26 | [MIT license](https://tldrlegal.com/license/mit-license) 27 | 28 | -------------------------------------------------------------------------------- /docs/zh/i18n/README.md: -------------------------------------------------------------------------------- 1 | # 多语言 2 | 3 | 组件内部默认使用中文,若希望使用其他语言,则需要进行多语言设置。 4 | 5 | ## element-ui配置 6 | 7 | 首先,由于组件的输入组件都来源于 `element-ui`,我们需要先设置 `element-ui` 的多语言配置,在 `main.js` 中: 8 | 9 | ``` js 10 | import Vue from 'vue' 11 | import ElementUI from 'element-ui' 12 | import locale from 'element-ui/lib/locale/lang/en' 13 | 14 | Vue.use(ElementUI, { locale }) 15 | ``` 16 | 17 | 更多的 `element-ui` 多语言配置可以查看 [element-ui多语言配置](https://element.faas.ele.me/#/zh-CN/component/i18n) 18 | 19 | ## 配置组件 20 | 21 | 组件默认自带了中文和英文语言包,可以通过 `lang` 属性切换 22 | 23 | ``` vue 24 | 31 | ``` 32 | 33 | 属性取值对应表为 34 | 35 | | 语言 | 值 | 36 | | ---- | ----- | 37 | | 中文 | zh_CN | 38 | | 英文 | en_US | 39 | 40 | ### 自定义语言包 41 | 42 | 组件提供 `languages` 属性供用户自行扩展语言包,`languages` 格式如下: 43 | 44 | ``` js 45 | { 46 | 'zh_CN': { 47 | addKeyPlaceholder: '请输入您想要的键值', 48 | addKeyButtonText: '添加键值', 49 | addArrayItemButtonText: '添加元素' 50 | }, 51 | 'en_US': { 52 | ... 53 | } 54 | } 55 | ``` 56 | 57 | ``` html 58 | 66 | ``` 67 | 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-dynamic-form-component", 3 | "version": "2.8.1", 4 | "license": "MIT", 5 | "author": { 6 | "email": "mail@quincychen.cn", 7 | "name": "quincychen", 8 | "url": "https://quincychen.cn" 9 | }, 10 | "keywords": [ 11 | "vue", 12 | "dynamic", 13 | "form", 14 | "component" 15 | ], 16 | "repository": { 17 | "type": "github", 18 | "url": "https://github.com/chenquincy/vue-dynamic-form-component" 19 | }, 20 | "scripts": { 21 | "dev": "vue-cli-service serve", 22 | "build": "vue-cli-service build --target lib --dest lib --name vue-dynamic-form-component ./packages/index.js", 23 | "docs:dev": "vuepress dev docs", 24 | "docs:build": "vuepress build docs" 25 | }, 26 | "main": "./lib/vue-dynamic-form-component.umd.min.js", 27 | "typings": "types/index.d.ts", 28 | "vetur": { 29 | "tags": "vetur/tags.json", 30 | "attributes": "vetur/attributes.json" 31 | }, 32 | "peerDependencies": { 33 | "element-ui": "^2.11.1", 34 | "vue": "^2.6.10" 35 | }, 36 | "devDependencies": { 37 | "@vue/cli-plugin-babel": "^3.8.0", 38 | "@vue/cli-plugin-eslint": "^3.8.0", 39 | "@vue/cli-service": "^3.8.0", 40 | "@vue/eslint-config-standard": "^4.0.0", 41 | "@vuepress/plugin-google-analytics": "^1.2.0", 42 | "babel-eslint": "^10.0.1", 43 | "element-ui": "^2.11.1", 44 | "eslint": "^5.16.0", 45 | "eslint-plugin-vue": "^5.0.0", 46 | "node-sass": "^7.0.0", 47 | "sass-loader": "^7.1.0", 48 | "vue": "^2.6.10", 49 | "vue-template-compiler": "^2.6.10", 50 | "vuepress": "^1.0.1", 51 | "vuepress-plugin-sitemap": "^2.1.2" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/dynamic-component/component.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 44 | 45 | 47 | -------------------------------------------------------------------------------- /packages/dynamic-component/index.js: -------------------------------------------------------------------------------- 1 | import DynamicComponent from './component.vue' 2 | 3 | DynamicComponent.install = function (Vue) { 4 | Vue.component(DynamicComponent.name, DynamicComponent) 5 | } 6 | 7 | export default DynamicComponent 8 | -------------------------------------------------------------------------------- /packages/dynamic-form-item/form-item.vue: -------------------------------------------------------------------------------- 1 | 102 | 103 | 230 | 231 | 267 | -------------------------------------------------------------------------------- /packages/dynamic-form-item/index.js: -------------------------------------------------------------------------------- 1 | import component from './form-item.vue' 2 | 3 | component.install = function (Vue) { 4 | Vue.component(component.name, component) 5 | } 6 | 7 | export default component 8 | -------------------------------------------------------------------------------- /packages/dynamic-form/form.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 194 | 195 | 208 | -------------------------------------------------------------------------------- /packages/dynamic-form/index.js: -------------------------------------------------------------------------------- 1 | import component from './form.vue' 2 | 3 | component.install = function (Vue) { 4 | Vue.component(component.name, component) 5 | } 6 | 7 | export default component 8 | -------------------------------------------------------------------------------- /packages/dynamic-input/index.js: -------------------------------------------------------------------------------- 1 | import DynamicInput from './input.vue' 2 | 3 | DynamicInput.install = function (Vue) { 4 | Vue.component(DynamicInput.name, DynamicInput) 5 | } 6 | 7 | export default DynamicInput 8 | -------------------------------------------------------------------------------- /packages/dynamic-input/input.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 143 | 144 | 152 | -------------------------------------------------------------------------------- /packages/i18n.js: -------------------------------------------------------------------------------- 1 | export default { 2 | en_US: { 3 | addKeyPlaceholder: 'Input the key you want to add', 4 | addKeyButtonText: 'Add Key', 5 | addArrayItemButtonText: 'Add Item' 6 | }, 7 | zh_CN: { 8 | addKeyPlaceholder: '输入想要添加的字段名', 9 | addKeyButtonText: '添加', 10 | addArrayItemButtonText: '添加数据项' 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/index.js: -------------------------------------------------------------------------------- 1 | import DynamicForm from './dynamic-form/index' 2 | 3 | const install = function (Vue) { 4 | if (install.installed) { 5 | return false 6 | } 7 | install.installed = true 8 | DynamicForm.install(Vue) 9 | } 10 | 11 | if (typeof window !== 'undefined' && window.Vue) { 12 | install(window.Vue) 13 | } 14 | 15 | export default DynamicForm 16 | -------------------------------------------------------------------------------- /packages/utils.js: -------------------------------------------------------------------------------- 1 | export function isComplexType (type) { 2 | return ['object', 'array'].includes(type) 3 | } 4 | 5 | /** 6 | * get font length of string 7 | * @param {String} str 8 | */ 9 | export function getStringLength (str) { 10 | let len = 0 11 | for (let i = 0; i < str.length; i++) { 12 | str.charCodeAt(i) > 255 ? len += 2 : len++ 13 | } 14 | return Math.round(len / 2) 15 | } 16 | 17 | /** 18 | * get longest key's font length of object 19 | * @param {Object} object 20 | */ 21 | export function getLabelWidth (descriptors, fontSize) { 22 | let maxLen = 0 23 | if (descriptors instanceof Array) { 24 | maxLen = getStringLength('Item ' + descriptors.length) 25 | } else { 26 | for (const key in descriptors) { 27 | if (descriptors[key]) { 28 | const typeDescriptor = findTypeDescriptor(descriptors[key]) 29 | maxLen = Math.max(maxLen, getStringLength(typeDescriptor.label || key)) 30 | } else { 31 | maxLen = Math.max(maxLen, getStringLength(key)) 32 | } 33 | } 34 | } 35 | return `${maxLen * fontSize + 30}px` // add 30px for required char '*' 36 | } 37 | 38 | /** 39 | * darken color with offset 40 | * @param {String} color // HEX color 41 | * @param {Integer} offset // offset to darken, offset should >= 0 42 | */ 43 | const DARKEST_COLOR = 150 44 | export function darkenColor (color, offset) { 45 | if (offset === 0) return color 46 | if (color[0] === '#') color = color.slice(1) 47 | offset = parseInt(offset) 48 | if (isNaN(offset)) return color 49 | 50 | offset = 0 - offset 51 | if (offset >= 0) return color 52 | 53 | const num = parseInt(color, 16) 54 | let r = (num >> 16) + offset 55 | let b = ((num >> 8) & 0x00FF) + offset 56 | let g = (num & 0x0000FF) + offset 57 | 58 | r = Math.max(DARKEST_COLOR, r) 59 | b = Math.max(DARKEST_COLOR, b) 60 | g = Math.max(DARKEST_COLOR, g) 61 | 62 | const newColor = g | (b << 8) | (r << 16) 63 | return `#${newColor.toString(16)}` 64 | } 65 | 66 | export function findTypeDescriptor (descriptor) { 67 | return descriptor instanceof Array ? descriptor.find(item => !!item.type) : descriptor 68 | } 69 | 70 | export function parseDescriptor (_descriptor) { 71 | let descriptor = findTypeDescriptor(_descriptor) 72 | if (['object', 'array'].includes(descriptor.type)) { 73 | if (descriptor.type === 'object') { 74 | // object 75 | if (descriptor.fields) { 76 | const data = {} 77 | for (const key in descriptor.fields) { 78 | data[key] = parseDescriptor(descriptor.fields[key]) 79 | } 80 | return data 81 | } else if (descriptor.defaultField) { 82 | // object is a hashmap 83 | return {} 84 | } 85 | } else { 86 | // array 87 | return [] 88 | } 89 | } else { 90 | return null 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-dynamic-form-component 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/vue-dynamic-form-component.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenquincy/vue-dynamic-form-component/8d061c21d7fb637a5117d3a65ee6cc438e348c48/public/vue-dynamic-form-component.gif -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export type ComponentSize = 'large' | 'medium' | 'small' | 'mini' 4 | 5 | export interface ValidateCallback { 6 | /** 7 | * The callback to tell the validation result 8 | * 9 | * @param isValid Whether the form is valid 10 | */ 11 | (isValid: boolean): void 12 | } 13 | 14 | declare class DynamicForm extends Vue { 15 | /* binding value */ 16 | value: object 17 | /* default language of component */ 18 | lang: string 19 | /* language packages of component */ 20 | languages: object 21 | /* descriptors of form, refer to https://github.com/yiminghe/async-validator */ 22 | descriptors: object 23 | /* size of component */ 24 | size: ComponentSize 25 | /* background color of component, default #FFFFFF */ 26 | backgroundColor: string 27 | /* font size of component, default 14 */ 28 | fontSize: number 29 | /* darken sub-form's background-color with offset while get positive integer, default 8 */ 30 | bgColorOffset: number 31 | /** 32 | * Validate the whole form 33 | * 34 | * @param callback A callback to tell the validation result 35 | */ 36 | validate (callback: ValidateCallback): void 37 | validate (): Promise 38 | 39 | /** reset all the fields and remove validation result */ 40 | resetFields (): void 41 | 42 | /** clear validation message for certain fields */ 43 | clearValidate (): void 44 | } 45 | 46 | export default DynamicForm -------------------------------------------------------------------------------- /vetur/attributes.json: -------------------------------------------------------------------------------- 1 | { 2 | "value": { 3 | "description": "value of form" 4 | }, 5 | "lang": { 6 | "options": ["zh_CN", "en_US", ""], 7 | "description": "Select the language of form or input the language key of custom languages, default: zh_CN" 8 | }, 9 | "languages": { 10 | "description": "Custom language packages, format refer to the docs." 11 | }, 12 | "descriptors": { 13 | "description": "FormData's descriptors, which will be used to generate the form." 14 | }, 15 | "size": { 16 | "options": ["medium", "small", "mini"], 17 | "description": "Size of component, default: small." 18 | }, 19 | "background-color": { 20 | "description": "Background color of form, default: #FFFFFF." 21 | }, 22 | "font-size": { 23 | "description": "Font size of form(for compute label's width), default: 14." 24 | }, 25 | "bg-color-offset": { 26 | "description": "Form background color offset(for distinguish the multi-level form), default 8." 27 | }, 28 | "show-outer-error": { 29 | "type": "boolean", 30 | "description": "Whether show the error of the parent component, default: true" 31 | } 32 | } -------------------------------------------------------------------------------- /vetur/tags.json: -------------------------------------------------------------------------------- 1 | { 2 | "dynamic-form": { 3 | "attributes": ["value", "lang", "languages", "descriptors", "size", "backgroundColor", "fontSize", "bgColorOffset", "showOuterError"], 4 | "defaults": ["v-model", "descriptors"], 5 | "description": "Generate form automatically by using descriptors." 6 | } 7 | } -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | lintOnSave: false, 3 | css: { 4 | extract: false 5 | }, 6 | pages: { 7 | index: { 8 | entry: 'dev/main.js', 9 | template: 'public/index.html', 10 | filename: 'index.html' 11 | } 12 | }, 13 | configureWebpack: config => { 14 | if (process.env.NODE_ENV === 'production') { 15 | config.externals = ['vue', 'Vue', 'element-ui', 'ElementUI'] 16 | } 17 | } 18 | } 19 | --------------------------------------------------------------------------------