├── assets
└── demo.gif
├── index.js
├── main-form.vue
├── package.json
├── readme.md
└── sub-form-mixin.js
/assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyPan/vue-complex-form/2538d310d3803eb79cf9d30c1fcc24e85908852b/assets/demo.gif
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | export { default as MainForm } from './main-form'
2 | export { default as SubFormMixin } from './sub-form-mixin'
3 |
--------------------------------------------------------------------------------
/main-form.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 | {{ form.name }}
14 |
22 |
23 |
24 |
25 |
32 |
33 |
34 |
35 |
239 |
240 |
243 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-complex-form",
3 | "version": "1.0.2",
4 | "description": "vue 复杂表单方案",
5 | "main": "index.js",
6 | "keywords": [
7 | "complex-form",
8 | "复杂表单",
9 | "大型表单",
10 | "vue",
11 | "element-ui"
12 | ],
13 | "author": "edwardpan2010@gmail.com",
14 | "license": "MIT",
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/EddyPan/vue-complex-form.git"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # vue-complex-form 复杂表单
2 |
3 | 该组件基于VUE和ElementUI设计开发,通过弹出Dialog对话框+标签页的形式,承载复杂表单的新建、编辑、克隆、详情展示等操作。本方案本质是将复杂表单拆分为多个子表单,通过分步填写、验证,提升页面交互体验,降低复杂表单开发门槛,提升开发效率。
4 | 包含MainForm组件和SubFormMixin混入两部分。
5 |
6 |  "表单演示")
7 |
8 | ## 引用依赖
9 | npm install vue-complex-form
10 |
11 | ## MainForm 主表单
12 |
13 | 提供复杂表单主入口,通过Tabs标签页进行分组子表单,整合表单验证、表单步骤、表单提交等页面交互功能,开发者仅需关注业务逻辑代码编写。
14 |
15 | ### 引入主表单组件
16 | ```javascript
17 | import { MainForm } from 'vue-complex-form'
18 |
19 | components: { MainForm }
20 | ```
21 |
22 | ### 引入相关子表单组件
23 | import SubFormComps from './pipeline-details/index'
24 |
25 | ### 编写html模板
26 |
27 | ```html
28 |
35 | ```
36 |
37 | ### 组装表单数据
38 | ```javascript
39 | // data数据为原始复杂表单数据,通过该方法组转换成相应的数据格式
40 | resolveDataToList(data) {
41 | return [
42 | {
43 | key: 'baseinfo',
44 | name: '基础信息',
45 | component: SubFormComps.BaseForm,
46 | data: {
47 | name: data.name,
48 | type: data.type
49 | },
50 | hidden: false,
51 | valid: true
52 | },
53 | {
54 | key: 'application',
55 | name: '选择应用',
56 | component: SubFormComps.AppForm,
57 | data: {
58 | application: data.application
59 | },
60 | hidden: false,
61 | valid: true
62 | },
63 | {
64 | key: 'build',
65 | name: '代码编译',
66 | component: SubFormComps.BuildForm,
67 | data: {
68 | build: data.build
69 | },
70 | // 可以自定义hidden方法,动态调整是否展示该子表单,linkChannel为表单数据监听通道
71 | hidden: linkChannel => (linkChannel.appType === 'container'),
72 | valid: true
73 | },
74 | {
75 | key: 'deploy',
76 | name: '部署信息',
77 | component: SubFormComps.DeployForm,
78 | data: {
79 | deployData: data.deployData
80 | },
81 | hidden: false,
82 | valid: true
83 | }
84 | ]
85 | }
86 | ```
87 |
88 | ### 提交数据
89 | ```javascript
90 | // 点击确认按钮,并且表单验证通过后会触发该方法
91 | handleSubmit(formData) {
92 | console.log(formData)
93 | // 提交数据到后台服务
94 |
95 |
96 | // 这里需要开发人员提交完数据后主动关闭表单
97 | this.showMainForm = false
98 | }
99 | ```
100 |
101 | ### 属性参数
102 |
103 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
104 | | -------------- | ---------------------------------------------------------------------------------- | ------- | --------------------------------- | -------- |
105 | | form-title | 表单名称,配合下面的form-type,自动组合为对话框名 | string | -- | '表单' |
106 | | form-type | 表单类型,当form-type为空时,显示对话框名即为表单名 | string | add
edit
clone
detail | '' |
107 | | pre-btn-txt | 上一步按钮文字,在非第一页展示 | string | -- | '上一步' |
108 | | next-btn-txt | 下一步按钮文字,在非最后一页展示 | string | -- | '下一步' |
109 | | submit-btn-txt | 提交表单按钮文字,在可编辑模式下最后一页展示 | string | -- | '确定' |
110 | | calcel-btn-txt | 取消表单按钮文字,在可编辑模式下展示 | string | -- | '取消' |
111 | | close-btn-txt | 关闭表单按钮文字,在不可编辑模式下展示 | string | -- | '关闭' |
112 | | append-to-body | Dialog 自身是否插入至 body 元素上。嵌套的 Dialog 必须指定该属性并赋值为 true | boolean | -- | false |
113 | | width | Dialog 的宽度 | string | -- | '50%' |
114 | | data | 复杂表单数据对象列表,每个对象代表一个子表单,具体对象字段定义,请查看表单数据对象 | array | -- | -- |
115 |
116 | ### 表单数据对象
117 |
118 | | 参数 | 说明 | 类型 | 默认值 |
119 | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------ |
120 | | key | 表单key,相当于本表单ID,必须唯一 | string | -- | -- |
121 | | name | 表单名称,用于当前Tab页面名称,尽量控制在6个字以内 | string | -- |
122 | | component | 引入的子表单模块 | component instance | -- |
123 | | data | 子表单数据,子表单内部会进行深度拷贝,不会对外层数据造成影响,详细信息参考SubFormMixin | object | -- |
124 | | hidden | 是否展示子表单,hidden为true时,该子表单不展示且数据不会被提交,如果表单是否隐藏受表单数据影响,可通过自定义方法实现动态调整,方法参数为linkChannel对象 | function \| boolean | false |
125 | | valid | 表单验证是否通过,请初始化默认值 | boolean | -- |
126 |
127 | ### 事件
128 |
129 | | 参数 | 说明 | 回调参数 |
130 | | ------ | ---------------------------- | ------------------------------------------- |
131 | | submit | 提交表单且数据验证通过后触发 | formData:object,有效子表单组装后的表单数据 |
132 | | closed | 表单对话框关闭后触发 | -- |
133 |
134 | ## SubForm 子表单
135 | 每个子表单需要开发人员按照实际需求开发,本方案将提供子表单mixin,开发人员只需关注业务逻辑编写。
136 |
137 | ### 引入子表单混入
138 |
139 | ```javascript
140 | import { SubFormMixin } from '@/components/ComplexForm'
141 |
142 | mixins: [SubFormMixin]
143 | ```
144 |
145 | ### 编写html模板
146 |
147 | **注意:** 如果某个字段需要和其他子表单通信,请主动emit field-change事件,通过linkChannel进行数据交互
148 |
149 | ```html
150 |
158 |
159 |
160 |
161 |
162 |
165 |
170 |
171 |
172 |
173 | ```
174 |
175 | ### 编写表单验证规则
176 | ```javascript
177 | rules: {
178 | name: [{ required: true, message: '请输入流水线名称', trigger: 'blur' }]
179 | }
180 | ```
181 |
182 | ### 子表单触发事件到父组件
183 | ```javascript
184 | this.$emit("event", "eventName", args1, args2, args3)
185 | ```
186 |
187 | ### 表单数据通信,监听表单数据变化
188 |
189 | ```javascript
190 | watch: {
191 | 'linkChannel.appType': {
192 | handler(v) {
193 | console.log(v)
194 | // 根据数据变化调整业务逻辑
195 | }
196 | }
197 | }
198 | ```
199 |
200 | ## SubFormMixin 子表单混入功能
201 | 子表单通用功能,主要负责数据拷贝、表单验证、linkChannel数据变化传递通道等功能,开发者仅需关注业务逻辑代码编写,无需过多关注混入所需参数,MainForm组件已封装相关参数的传递和逻辑处理。
202 |
203 | ### 注入参数
204 |
205 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
206 | | ------------ | -------- | ------- | ------------------------ | ------ |
207 | | formType | 表单类型 | String | add、edit、clone、detail | -- |
208 | | formDisabled | 禁用表单 | boolean | -- | -- |
209 |
210 | ### 属性参数
211 |
212 | | 参数 | 说明 | 类型 | 默认值 |
213 | | ------------ | ------------------------------------------------------ | ------ | ------ |
214 | | form-key | 表单Key,相当于表单ID,同一组子表单中的formKey不可重复 | string | -- |
215 | | data | 表单数据 | object | {} |
216 | | link-channel | 表单字段变动监听通道 | object | {} |
217 |
218 | ## 鸣谢
219 | 感谢[@why19](https://juejin.cn/user/1794018999272525)的无私奉献,本方案主要分灵感来自于why19的[vue+element大型表单解决方案](https://juejin.cn/post/6964330851173662757)
220 |
--------------------------------------------------------------------------------
/sub-form-mixin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 主表单mixin
3 | */
4 | import _ from 'lodash'
5 | export default {
6 | inject: ['formType', 'formDisabled'],
7 | props: {
8 | formKey: {
9 | type: String
10 | },
11 | data: {
12 | type: Object,
13 | default: () => ({})
14 | },
15 | linkChannel: {
16 | type: Object,
17 | default: () => ({})
18 | }
19 | },
20 | data() {
21 | return {
22 | formData: {},
23 | ruleResults: {}
24 | }
25 | },
26 | computed: {
27 | },
28 | watch: {
29 | data: {
30 | handler(newValue) {
31 | this.formData = _.cloneDeep(newValue) || {}
32 | },
33 | immediate: true
34 | }
35 | },
36 | methods: {
37 | validForm() {
38 | let result = false
39 | this.$refs['form'].validate((valid) => { result = valid })
40 | return result
41 | },
42 | handleValidate(rule, isValid) {
43 | // 记录表单项结果,用于判断是否所有项都通过
44 | this.ruleResults[rule] = isValid
45 | if (!isValid) {
46 | // 只要本项失败,则通知主表单校验失败
47 | this.$emit('validate', this.formKey, false)
48 | } else {
49 | let result = true
50 | // 如果存在未通过的项,则本表单校验未通过
51 | for (const key in this.ruleResults) {
52 | if (!this.ruleResults[key]) {
53 | result = false
54 | break
55 | }
56 | }
57 | // 当不存在未通过的项时,通知主表单校验通过
58 | if (result) {
59 | this.$emit('validate', this.formKey, true)
60 | }
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------