Commit 35c37891c3192ed7fe12449960675de991579827
1 parent
1416fdbe
Exists in
master
and in
1 other branch
调整新式表单逻辑
Showing
5 changed files
with
199 additions
and
145 deletions
Show diff stats
| ... | ... | @@ -0,0 +1,81 @@ |
| 1 | +<template> | |
| 2 | + <!-- 在row上使用flex,防止表单组件大小不一导致错位 --> | |
| 3 | + <el-row type="flex" style="flex-wrap: wrap;" :class="contentClass || 'eagle-form__group-content'"> | |
| 4 | + <template v-for="(item, index) in list"> | |
| 5 | + <!-- 表单项有设置分组时 --> | |
| 6 | + <el-col v-if="item.group && item.list" :key="index" :span="item.group.span || 24"> | |
| 7 | + <el-row type="flex" style="flex-wrap: wrap;"> | |
| 8 | + <!-- 表单分组标题 --> | |
| 9 | + <el-col :class="titleClass || 'eagle-form__group-title'" v-if="item.group.title" :span="24">{{ item.group.title || item.group }}</el-col> | |
| 10 | + <!-- 递归本组件 --> | |
| 11 | + <form-render :title-class="titleClass" :list="item.list" :model="itemKey ? model[itemKey] || {} : model" @item-change="onItemChange" @form-item-change="onFormItemChange" :itemKey="item.group.key"></form-render> | |
| 12 | + </el-row> | |
| 13 | + </el-col> | |
| 14 | + <!-- 正常无分组表单项 --> | |
| 15 | + <el-col v-else :span="item.span || 12" :key="index"> | |
| 16 | + <el-form-item :label="item.label" :label-width="item.labelWidth || '120px'" :prop="item.fullKey" :rules="item.rules"> | |
| 17 | + <!-- 自定义组件 --> | |
| 18 | + <component :is="item.type" :value="itemValue(item)" @input="v => onInput({ value: v, item })"></component> | |
| 19 | + </el-form-item> | |
| 20 | + </el-col> | |
| 21 | + </template> | |
| 22 | + </el-row> | |
| 23 | +</template> | |
| 24 | + | |
| 25 | +<script> | |
| 26 | +export default { | |
| 27 | + name: 'form-render', | |
| 28 | + props: { | |
| 29 | + list: Array, | |
| 30 | + model: Object, | |
| 31 | + itemKey: String, | |
| 32 | + titleClass: String, | |
| 33 | + contentClass: String, | |
| 34 | + }, | |
| 35 | + methods: { | |
| 36 | + /** | |
| 37 | + * @description 根据表单项的key查询该值 | |
| 38 | + * @param {Object} item 表单项配置 | |
| 39 | + * @returns {Any} 返回值 | |
| 40 | + */ | |
| 41 | + itemValue(item) { | |
| 42 | + if (this.itemKey) { // 如果存在itemKey,即当前项位于嵌套分组内,查询分组名下对应key的值 | |
| 43 | + const groupItem = this.model[this.itemKey] || {}; | |
| 44 | + return groupItem[item.key]; | |
| 45 | + } else { // 否则即意味着不在分组内,直接查询model下对应key的值 | |
| 46 | + return this.model[item.key]; | |
| 47 | + } | |
| 48 | + }, | |
| 49 | + /** | |
| 50 | + * @description 组件有值输入时的事件 | |
| 51 | + * @param {Object} data { value => '组件值'; item => '表单项配置' } | |
| 52 | + */ | |
| 53 | + onInput({ value, item }) { | |
| 54 | + if (this.itemKey) { | |
| 55 | + this.$emit('item-change', { [this.itemKey]: { ...this.model[this.itemKey], [item.key]: value } }); | |
| 56 | + } else { | |
| 57 | + this.$emit('item-change', { [item.key]: value }); | |
| 58 | + } | |
| 59 | + this.$emit('form-item-change', { [item.fullKey]: value }); | |
| 60 | + }, | |
| 61 | + /** | |
| 62 | + * @description 当表单项有改动时的事件 | |
| 63 | + * @param {Any} 表单项值 | |
| 64 | + */ | |
| 65 | + onItemChange(value) { | |
| 66 | + if (this.itemKey) { | |
| 67 | + this.$emit('item-change', { [this.itemKey]: { ...this.model[this.itemKey], ...value } }); | |
| 68 | + } else { | |
| 69 | + this.$emit('item-change', value); | |
| 70 | + } | |
| 71 | + }, | |
| 72 | + /** | |
| 73 | + * @description 当表单项校验值有改动时的事件 | |
| 74 | + * @param {Any} 表单项校验值 | |
| 75 | + */ | |
| 76 | + onFormItemChange(value) { | |
| 77 | + this.$emit('form-item-change', value); | |
| 78 | + } | |
| 79 | + } | |
| 80 | +} | |
| 81 | +</script> | ... | ... |
| ... | ... | @@ -0,0 +1,103 @@ |
| 1 | +<template> | |
| 2 | + <el-form ref="form" size="small" :class="formClass" :model="formModel"> | |
| 3 | + {{ formModel }} | |
| 4 | + <form-render :title-class="titleClass" :content-class="contentClass" :list="formList" :model="model" @item-change="onItemChange" @form-item-change="onFormItemChange"></form-render> | |
| 5 | + {{ list }} | |
| 6 | + </el-form> | |
| 7 | +</template> | |
| 8 | + | |
| 9 | +<script> | |
| 10 | +import FormRender from './form-render'; | |
| 11 | +import { cloneDeep } from 'lodash'; | |
| 12 | + | |
| 13 | +export default { | |
| 14 | + name: 'FormNew', | |
| 15 | + components: { FormRender }, | |
| 16 | + props: { | |
| 17 | + value: Object, | |
| 18 | + list: Array, | |
| 19 | + formClass: String, | |
| 20 | + titleClass: String, | |
| 21 | + contentClass: String, | |
| 22 | + }, | |
| 23 | + data() { | |
| 24 | + return { | |
| 25 | + model: {}, | |
| 26 | + formModel: {}, | |
| 27 | + } | |
| 28 | + }, | |
| 29 | + watch: { | |
| 30 | + value(val = {}) { | |
| 31 | + this.model = val; | |
| 32 | + this.setFormModel(val) | |
| 33 | + }, | |
| 34 | + }, | |
| 35 | + computed: { | |
| 36 | + formList() { | |
| 37 | + // 深度克隆传入的列表,避免原始值被修改 | |
| 38 | + const newList = cloneDeep(this.list); | |
| 39 | + // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key | |
| 40 | + const generateFullKey = (list, parentKey) => { | |
| 41 | + list.forEach(item => { | |
| 42 | + if (item.group && item.list) { | |
| 43 | + if (item.group.key) { | |
| 44 | + item.fullKey = `${parentKey ? `${parentKey}-${item.group.key}` : item.group.key}`; | |
| 45 | + } else { | |
| 46 | + item.fullKey = item.key; | |
| 47 | + } | |
| 48 | + generateFullKey(item.list, item.fullKey); | |
| 49 | + } else { | |
| 50 | + item.fullKey = `${parentKey ? `${parentKey}-${item.key}` : item.key}`; | |
| 51 | + } | |
| 52 | + }); | |
| 53 | + }; | |
| 54 | + generateFullKey(newList); | |
| 55 | + return newList; | |
| 56 | + } | |
| 57 | + }, | |
| 58 | + methods: { | |
| 59 | + /** | |
| 60 | + * @description 表单项值变化 | |
| 61 | + * @param {Object} item 表单项值对象 | |
| 62 | + */ | |
| 63 | + onItemChange(item) { | |
| 64 | + this.$emit('input', { ...this.model, ...item }); | |
| 65 | + }, | |
| 66 | + /** | |
| 67 | + * @description 表单相校验值变化 | |
| 68 | + * @param {Object} item 表单项校验值对象 | |
| 69 | + */ | |
| 70 | + onFormItemChange(item) { | |
| 71 | + this.formModel = { ...this.formModel, ...item }; | |
| 72 | + }, | |
| 73 | + /** | |
| 74 | + * @description 校验表单 | |
| 75 | + */ | |
| 76 | + validate() { | |
| 77 | + this.$refs.form.validate(valid => { | |
| 78 | + this.$emit("validate", valid); | |
| 79 | + }); | |
| 80 | + }, | |
| 81 | + /** | |
| 82 | + * @description 根据表单值设置表单校验值 | |
| 83 | + * @param {Object} value 表单值 | |
| 84 | + */ | |
| 85 | + setFormModel(value) { | |
| 86 | + let formModel = {}; | |
| 87 | + // 递归深度解析表单值,将表单值扁平化为一层的对象并设置为表单校验值对象 | |
| 88 | + const setFormModelValue = (list, parentKey) => { | |
| 89 | + list.forEach(item => { | |
| 90 | + item.fullKey = `${parentKey ? `${parentKey}-${item.key}` : item.key}`; | |
| 91 | + if (item.value instanceof Object) { | |
| 92 | + setFormModelValue(Object.keys(item.value).map(key => ({ key, value: item.value[key] })), item.fullKey); | |
| 93 | + } else { | |
| 94 | + formModel[item.fullKey] = item.value; | |
| 95 | + } | |
| 96 | + }); | |
| 97 | + }; | |
| 98 | + setFormModelValue(Object.keys(value).map(key => ({ key, value: value[key] }))); | |
| 99 | + this.formModel = formModel; | |
| 100 | + } | |
| 101 | + } | |
| 102 | +} | |
| 103 | +</script> | ... | ... |
examples/views/page/form/form-render.vue
| ... | ... | @@ -1,56 +0,0 @@ |
| 1 | -<template> | |
| 2 | - <el-row :gutter="15" type="flex" style="flex-wrap: wrap;"> | |
| 3 | - <template v-for="(item, index) in list"> | |
| 4 | - <el-col v-if="item.group && item.list" :key="index" :span="item.group.span || 24"> | |
| 5 | - <el-row :gutter="15" type="flex" style="flex-wrap: wrap;"> | |
| 6 | - <el-col v-if="item.group.title" :span="24">{{ item.group.title || item.group }}</el-col> | |
| 7 | - <form-render :list="item.list" :model="itemKey ? model[itemKey] || {} : model" @item-change="onItemChange" @form-item-change="onFormItemChange" :itemKey="item.group.key"></form-render> | |
| 8 | - </el-row> | |
| 9 | - </el-col> | |
| 10 | - <el-col v-else :span="item.span || 12" :key="index"> | |
| 11 | - <el-form-item :label="item.label" :label-width="item.labelWidth || '120px'" :prop="item.fullKey" :rules="item.rules"> | |
| 12 | - <component :is="item.type" :value="itemValue({ item })" @input="v => handleInput({ value: v, item })"></component> | |
| 13 | - </el-form-item> | |
| 14 | - </el-col> | |
| 15 | - </template> | |
| 16 | - </el-row> | |
| 17 | -</template> | |
| 18 | - | |
| 19 | -<script> | |
| 20 | -export default { | |
| 21 | - name: 'form-render', | |
| 22 | - props: { | |
| 23 | - list: Array, | |
| 24 | - model: Object, | |
| 25 | - itemKey: String, | |
| 26 | - }, | |
| 27 | - methods: { | |
| 28 | - itemValue({ item }) { | |
| 29 | - if (this.itemKey) { | |
| 30 | - const groupItem = this.model[this.itemKey] || {}; | |
| 31 | - return groupItem[item.key]; | |
| 32 | - } else { | |
| 33 | - return this.model[item.key]; | |
| 34 | - } | |
| 35 | - }, | |
| 36 | - handleInput({ value, item }) { | |
| 37 | - if (this.itemKey) { | |
| 38 | - this.$emit('item-change', { [this.itemKey]: { ...this.model[this.itemKey], [item.key]: value } }); | |
| 39 | - } else { | |
| 40 | - this.$emit('item-change', { [item.key]: value }); | |
| 41 | - } | |
| 42 | - this.$emit('form-item-change', { [item.fullKey]: value }); | |
| 43 | - }, | |
| 44 | - onItemChange(value) { | |
| 45 | - if (this.itemKey) { | |
| 46 | - this.$emit('item-change', { [this.itemKey]: { ...this.model[this.itemKey], ...value } }); | |
| 47 | - } else { | |
| 48 | - this.$emit('item-change', value); | |
| 49 | - } | |
| 50 | - }, | |
| 51 | - onFormItemChange(value) { | |
| 52 | - this.$emit('form-item-change', value); | |
| 53 | - } | |
| 54 | - } | |
| 55 | -} | |
| 56 | -</script> | |
| 57 | 0 | \ No newline at end of file |
examples/views/page/form/index.vue
| ... | ... | @@ -1,86 +0,0 @@ |
| 1 | -<template> | |
| 2 | - <el-form ref="form" size="small" :model="formModel"> | |
| 3 | - {{ formModel }} | |
| 4 | - <form-render :list="formList" :model="model" @item-change="onItemChange" @form-item-change="onFormItemChange"></form-render> | |
| 5 | - </el-form> | |
| 6 | -</template> | |
| 7 | - | |
| 8 | -<script> | |
| 9 | -import FormRender from './form-render'; | |
| 10 | - | |
| 11 | -export default { | |
| 12 | - components: { FormRender }, | |
| 13 | - props: { | |
| 14 | - value: Object, | |
| 15 | - list: Array, | |
| 16 | - }, | |
| 17 | - data() { | |
| 18 | - return { | |
| 19 | - model: {}, | |
| 20 | - formModel: {} | |
| 21 | - } | |
| 22 | - }, | |
| 23 | - watch: { | |
| 24 | - value(val) { | |
| 25 | - const data = val || {}; | |
| 26 | - this.model = data; | |
| 27 | - let formModel = {}; | |
| 28 | - const setFormModelValue = (list, parentKey) => { | |
| 29 | - list.forEach(item => { | |
| 30 | - item.fullKey = `${parentKey ? `${parentKey}-${item.key}` : item.key}`; | |
| 31 | - if (item.value instanceof Object) { | |
| 32 | - setFormModelValue(Object.keys(item.value).map(key => ({ key, value: item.value[key] })), item.fullKey); | |
| 33 | - } else { | |
| 34 | - if (item.fullKey) { | |
| 35 | - formModel[item.fullKey] = item.value; | |
| 36 | - } | |
| 37 | - } | |
| 38 | - }); | |
| 39 | - }; | |
| 40 | - setFormModelValue(Object.keys(data).map(key => ({ key, value: data[key] }))); | |
| 41 | - this.formModel = formModel; | |
| 42 | - }, | |
| 43 | - }, | |
| 44 | - computed: { | |
| 45 | - formList() { | |
| 46 | - const newList = [...new Set(this.list)] | |
| 47 | - const generateFullKey = (list, parentKey) => { | |
| 48 | - list.forEach(item => { | |
| 49 | - if (item.group && item.list) { | |
| 50 | - if (item.group.key) { | |
| 51 | - item.fullKey = `${parentKey ? `${parentKey}-${item.group.key}` : item.group.key}`; | |
| 52 | - } else { | |
| 53 | - item.fullKey = item.key; | |
| 54 | - } | |
| 55 | - generateFullKey(item.list, item.fullKey); | |
| 56 | - } else { | |
| 57 | - item.fullKey = `${parentKey ? `${parentKey}-${item.key}` : item.key}`; | |
| 58 | - } | |
| 59 | - }); | |
| 60 | - }; | |
| 61 | - generateFullKey(newList); | |
| 62 | - return newList; | |
| 63 | - } | |
| 64 | - }, | |
| 65 | - methods: { | |
| 66 | - // 表单项值变化 | |
| 67 | - onItemChange(item) { | |
| 68 | - this.$emit('input', { ...this.model, ...item }); | |
| 69 | - }, | |
| 70 | - // 表单相校验值变化 | |
| 71 | - onFormItemChange(item) { | |
| 72 | - this.formModel = { ...this.formModel, ...item }; | |
| 73 | - }, | |
| 74 | - // 校验表单 | |
| 75 | - validate() { | |
| 76 | - this.$refs.form.validate(valid => { | |
| 77 | - this.$emit("validate", valid); | |
| 78 | - }); | |
| 79 | - }, | |
| 80 | - } | |
| 81 | -} | |
| 82 | -</script> | |
| 83 | - | |
| 84 | -<style> | |
| 85 | - | |
| 86 | -</style> | |
| 87 | 0 | \ No newline at end of file |
examples/views/page/other.vue
| 1 | +<style> | |
| 2 | +.custom-form { | |
| 3 | + background: #cfc; | |
| 4 | +} | |
| 5 | +.custom-title { | |
| 6 | + background: #ccf; | |
| 7 | +} | |
| 8 | +.custom-content { | |
| 9 | + background: #fcc; | |
| 10 | +} | |
| 11 | +</style> | |
| 12 | + | |
| 1 | 13 | <template> |
| 2 | 14 | <div> |
| 3 | 15 | <p>这是一个非markdown页面</p> |
| 4 | 16 | <pre>{{ model }}</pre> |
| 5 | 17 | <el-button size="mini" @click="handleGetValue">校验</el-button> |
| 6 | - <eg-form ref="form" v-model="model" :list="option.list" @validate="onValidate"></eg-form> | |
| 18 | + <eg-form ref="form" v-model="model" :list="option.list" @validate="onValidate" form-class="custom-form" title-class="custom-title" content-class="custom-content"></eg-form> | |
| 7 | 19 | </div> |
| 8 | 20 | </template> |
| 9 | 21 | |
| 10 | 22 | <script> |
| 11 | -import EgForm from './form'; | |
| 23 | +import EgForm from './form-new'; | |
| 12 | 24 | |
| 13 | 25 | export default { |
| 14 | 26 | name: 'other', |
| ... | ... | @@ -95,4 +107,4 @@ export default { |
| 95 | 107 | } |
| 96 | 108 | } |
| 97 | 109 | } |
| 98 | -</script> | |
| 99 | 110 | \ No newline at end of file |
| 111 | +</script> | ... | ... |