Commit 5bbd40ed2fc288108fa039dffa3a6d3c35236982

Authored by Aaron
1 parent 2db4f03a
Exists in master and in 1 other branch legacy

新式表单支持两种布局模式

examples/views/page/form-new/form-render.vue
1 <template> 1 <template>
2 <!-- 在row上使用flex,防止表单组件大小不一导致错位 --> 2 <!-- 在row上使用flex,防止表单组件大小不一导致错位 -->
3 - <el-row type="flex" style="flex-wrap: wrap;width: 100%;" :class="contentClass || 'eagle-form__group-content'"> 3 + <component :is="rowComponent" class="eagle-form__flex-wrap" :class="contentClass || 'eagle-form__group-content'">
4 <template v-for="(item, index) in list"> 4 <template v-for="(item, index) in list">
5 <!-- 表单项有设置分组时 --> 5 <!-- 表单项有设置分组时 -->
6 - <el-col v-if="item.group && item.list" :key="index" :span="item.group.span || span">  
7 - <el-row type="flex" style="flex-wrap: wrap;width: 100%;"> 6 + <component :is="colComponent" v-if="item.group && item.list" :key="index"
  7 + :span="type === 'div' ? undefined : item.group.span || 24" :style="{ width: type === 'div' ? '100%' : undefined }"
  8 + >
  9 + <component :is="rowComponent" class="eagle-form__flex-wrap">
8 <!-- 表单分组标题 --> 10 <!-- 表单分组标题 -->
9 - <el-col :class="titleClass || 'eagle-form__group-title'" v-if="item.group.title" :span="24">{{ item.group.title || item.group }}</el-col> 11 + <component :is="rowComponent" :class="titleClass || 'eagle-form__group-title'" v-if="item.group.title" style="width: 100%;">
  12 + {{ item.group.title || item.group }}
  13 + </component>
10 <!-- 递归本组件 --> 14 <!-- 递归本组件 -->
11 - <form-render :title-class="titleClass" :list="item.list" :value="value" :model="itemKey ? model[itemKey] || {} : model" @item-change="onItemChange" @form-item-change="onFormItemChange" :itemKey="item.group.key" @item-update="onItemUpdate"></form-render>  
12 - </el-row>  
13 - </el-col> 15 + <form-render :title-class="titleClass" :item-class="itemClass" :list="item.list" :value="value"
  16 + :model="itemKey ? model[itemKey] || {} : model" :itemKey="item.group.key" :type="type"
  17 + @item-change="onItemChange" @form-item-change="onFormItemChange" @item-update="onItemUpdate"
  18 + :span="type === 'div' ? undefined : span * (24 / (item.group.span || 24))"
  19 + ></form-render>
  20 + </component>
  21 + </component>
14 <!-- 正常无分组表单项 --> 22 <!-- 正常无分组表单项 -->
15 - <el-col v-else :span="item.span || span" :key="index">  
16 - <el-form-item :label="item.label" :label-width="item.labelWidth || '120px'" :prop="item.fullKey" :rules="item.rules"> 23 + <component :is="colComponent" v-else :span="type === 'div' ? undefined : item.span || span" :key="index"
  24 + :style="{ width: type === 'div' && item.style && item.style.width.indexOf('%') > -1 ? item.style.width : undefined, paddingRight: '10px' }"
  25 + >
  26 + <el-form-item :label="item.label" :label-width="item.labelWidth" :prop="item.fullKey" :rules="item.rules" :class="itemClass || 'eagle-form__item'">
17 <!-- 自定义组件 --> 27 <!-- 自定义组件 -->
18 - <component :is="item.type" :value="itemValue(item)" @input="v => onInput({ value: v, item })" v-on="bindItemEvent(item)"></component> 28 + <component :is="item.type" :value="itemValue(item)" @input="v => onInput({ value: v, item })"
  29 + v-on="bindItemEvent(item)" v-bind="item.props" :style="item.style || { maxWidth: '100%' }"
  30 + ></component>
19 </el-form-item> 31 </el-form-item>
20 - </el-col> 32 + </component>
21 </template> 33 </template>
22 - </el-row> 34 + </component>
23 </template> 35 </template>
24 36
25 <script> 37 <script>
@@ -32,8 +44,18 @@ export default { @@ -32,8 +44,18 @@ export default {
32 itemKey: String, 44 itemKey: String,
33 titleClass: String, 45 titleClass: String,
34 contentClass: String, 46 contentClass: String,
  47 + itemClass: String,
  48 + type: String,
35 span: Number, 49 span: Number,
36 }, 50 },
  51 + computed: {
  52 + rowComponent() {
  53 + return this.type === 'div' ? 'div' : 'el-row';
  54 + },
  55 + colComponent() {
  56 + return this.type === 'div' ? 'div' : 'el-col';
  57 + },
  58 + },
37 methods: { 59 methods: {
38 /** 60 /**
39 * @description 根据表单项的key查询该值 61 * @description 根据表单项的key查询该值
examples/views/page/form-new/index.vue
  1 +<style>
  2 +.el-input .el-input__inner, .el-textarea__inner {
  3 + border-radius: 0;
  4 +}
  5 +</style>
  6 +
  7 +<style>
  8 +.eagle-form__flex-wrap {
  9 + display: flex;
  10 + flex-wrap: wrap;
  11 + width: 100%;
  12 +}
  13 +.eagle-form__group-title {
  14 + font-weight: bold;
  15 + padding: 15px 5px;
  16 + border-bottom: 1px solid #d9d9d9;
  17 + margin-bottom: 30px;
  18 +}
  19 +.eagle-form__group-content {
  20 + margin: 15px 0px;
  21 +}
  22 +</style>
  23 +
1 <template> 24 <template>
2 - <el-form ref="form" size="small" :class="formClass" :model="formModel"> 25 + <el-form ref="form" size="mini" :class="formClass" :model="formModel" :label-width="labelWidth" :label-position="labelPosition || labelWidth ? 'right' : 'top'">
3 {{ formModel }} 26 {{ formModel }}
4 - <form-render :title-class="titleClass" :content-class="contentClass" :list="formList" :value="model" :model="model" :span="span"  
5 - @item-change="onItemChange" @form-item-change="onFormItemChange" @item-update="onItemUpdate"></form-render> 27 + <form-render :title-class="titleClass" :content-class="contentClass" :item-class="itemClass" :list="formList" :value="model"
  28 + :model="model" :span="span" :type="type"
  29 + @item-change="onItemChange" @form-item-change="onFormItemChange" @item-update="onItemUpdate"
  30 + ></form-render>
6 {{ list }} 31 {{ list }}
7 </el-form> 32 </el-form>
8 </template> 33 </template>
@@ -20,6 +45,10 @@ export default { @@ -20,6 +45,10 @@ export default {
20 formClass: String, 45 formClass: String,
21 titleClass: String, 46 titleClass: String,
22 contentClass: String, 47 contentClass: String,
  48 + itemClass: String,
  49 + labelWidth: String,
  50 + labelPosition: String,
  51 + type: String,
23 span: { 52 span: {
24 type: Number, 53 type: Number,
25 default: 24 54 default: 24
@@ -85,7 +114,7 @@ export default { @@ -85,7 +114,7 @@ export default {
85 */ 114 */
86 validate() { 115 validate() {
87 this.$refs.form.validate(valid => { 116 this.$refs.form.validate(valid => {
88 - this.$emit("validate", valid); 117 + this.$emit("validate", valid, this.model);
89 }); 118 });
90 }, 119 },
91 /** 120 /**
@@ -112,6 +141,7 @@ export default { @@ -112,6 +141,7 @@ export default {
112 * @description 手动更新某一表单项的值 141 * @description 手动更新某一表单项的值
113 * @param {Object} param 需要更新的参数对象或者对象数组 { name => 表单项key,可嵌套; value => 更新的值 } 142 * @param {Object} param 需要更新的参数对象或者对象数组 { name => 表单项key,可嵌套; value => 更新的值 }
114 * @example { name: 'a.b.c', value: 123 } 143 * @example { name: 'a.b.c', value: 123 }
  144 + * @example { name: 'd.0.e', value: ['f'] }
115 */ 145 */
116 onItemUpdate(param) { 146 onItemUpdate(param) {
117 this.$nextTick(() => { 147 this.$nextTick(() => {
examples/views/page/form-new/util.js
  1 +/**
  2 + * 深度克隆对象
  3 + * @param {Object} obj 目标对象
  4 + * @returns {Object} 克隆的新对象
  5 + */
1 export const cloneDeep = (obj) => { 6 export const cloneDeep = (obj) => {
2 if (typeof obj !== 'object') { 7 if (typeof obj !== 'object') {
3 return obj; 8 return obj;
@@ -35,19 +40,31 @@ export const cloneDeep = (obj) =&gt; { @@ -35,19 +40,31 @@ export const cloneDeep = (obj) =&gt; {
35 return newObj; 40 return newObj;
36 }; 41 };
37 42
  43 +/**
  44 + * 对象深度取值
  45 + * @desctiption 来源于"typy.js"中src/util的getNestedObject函数
  46 + * @param {Object} obj 目标对象
  47 + * @param {String} dotSeparatedKeys 用分隔符".", "[", "]", "'", """隔开的取值路径
  48 + * @example get({ a: { b: { c: ['d'] } } }, 'a.b.c.0')
  49 + */
38 export const get = (obj, dotSeparatedKeys) => { 50 export const get = (obj, dotSeparatedKeys) => {
39 if (dotSeparatedKeys !== undefined && typeof dotSeparatedKeys !== 'string') return undefined; 51 if (dotSeparatedKeys !== undefined && typeof dotSeparatedKeys !== 'string') return undefined;
40 if (typeof obj !== 'undefined' && typeof dotSeparatedKeys === 'string') { 52 if (typeof obj !== 'undefined' && typeof dotSeparatedKeys === 'string') {
41 - // split on ".", "[", "]", "'", """ and filter out empty elements  
42 - const splitRegex = /[.\[\]'"]/g; // eslint-disable-line no-useless-escape 53 + const splitRegex = /[.\[\]'"]/g;
43 const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== ''); 54 const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== '');
44 -  
45 - // eslint-disable-next-line no-param-reassign, no-confusing-arrow  
46 obj = pathArr.reduce((o, key) => (o && o[key] !== undefined ? o[key] : undefined), obj); 55 obj = pathArr.reduce((o, key) => (o && o[key] !== undefined ? o[key] : undefined), obj);
47 } 56 }
48 return obj; 57 return obj;
49 }; 58 };
50 59
  60 +/**
  61 + * 对象深度赋值
  62 + * @description 改写自get方法
  63 + * @param {Object} obj 目标对像
  64 + * @param {String} dotSeparatedKeys 用分隔符".", "[", "]", "'", """隔开的赋值路径
  65 + * @param {*} value 目标值
  66 + * @example set(obj, 'a.b.c', 'd')
  67 + */
51 export const set = (obj, dotSeparatedKeys, value) => { 68 export const set = (obj, dotSeparatedKeys, value) => {
52 const splitRegex = /[.\[\]'"]/g; 69 const splitRegex = /[.\[\]'"]/g;
53 const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== ''); 70 const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== '');
@@ -58,7 +75,6 @@ export const set = (obj, dotSeparatedKeys, value) =&gt; { @@ -58,7 +75,6 @@ export const set = (obj, dotSeparatedKeys, value) =&gt; {
58 } 75 }
59 return o[k] 76 return o[k]
60 }, obj)[key] = value 77 }, obj)[key] = value
61 - // pathArr.reduce((o, key) => (o && o[key] !== 'undefined' ? o[key] : undefined), obj)[key] = value  
62 }; 78 };
63 79
64 export default { 80 export default {
examples/views/page/other.vue
@@ -15,7 +15,11 @@ @@ -15,7 +15,11 @@
15 <p>这是一个非markdown页面</p> 15 <p>这是一个非markdown页面</p>
16 <pre>{{ model }}</pre> 16 <pre>{{ model }}</pre>
17 <el-button size="mini" @click="handleGetValue">校验</el-button> 17 <el-button size="mini" @click="handleGetValue">校验</el-button>
18 - <eg-form ref="form" v-model="model" :list="option.list" @validate="onValidate" :span="12" form-class="custom-form" title-class="custom-title" content-class="custom-content"></eg-form> 18 + <!-- <eg-form ref="form" v-model="model" :list="option.list" @validate="onValidate" label-width="80px" :span="6" type="div"></eg-form> -->
  19 + <eg-form
  20 + ref="form" v-model="model" :list="option.list" @validate="onValidate" :span="4"
  21 + form-class="custom-form" title-class="custom-title" content-class="custom-content" item-class="custom-item"
  22 + ></eg-form>
19 </div> 23 </div>
20 </template> 24 </template>
21 25
@@ -36,14 +40,14 @@ export default { @@ -36,14 +40,14 @@ export default {
36 option: { 40 option: {
37 list: [ 41 list: [
38 { 42 {
39 - group: { title: '基础信息' }, 43 + group: { title: '基础信息', span: 12 },
40 list: [ 44 list: [
41 { type: 'el-input', label: '名称', key: 'name' }, 45 { type: 'el-input', label: '名称', key: 'name' },
42 { type: 'el-input-number', label: '年龄', key: 'age' }, 46 { type: 'el-input-number', label: '年龄', key: 'age' },
43 ], 47 ],
44 }, 48 },
45 { 49 {
46 - group: { title: '' }, 50 + group: { title: '', span: 12 },
47 }, 51 },
48 { 52 {
49 group: { title: '住址', key: 'location', span: 24 }, 53 group: { title: '住址', key: 'location', span: 24 },
@@ -52,19 +56,20 @@ export default { @@ -52,19 +56,20 @@ export default {
52 { 56 {
53 group: { key: 'district' }, 57 group: { key: 'district' },
54 list: [ 58 list: [
55 - { type: 'el-input', label: '省', key: 'province', rules: [{ required: true, message: '请输入省' }], span: 12 },  
56 - { type: 'el-input', label: '市', key: 'city', span: 12 }, 59 + { type: 'el-input', label: '省', key: 'province', rules: [{ required: true, message: '请输入省' }] },
  60 + { type: 'el-input', label: '市', key: 'city' },
57 ], 61 ],
58 }, 62 },
59 { 63 {
60 group: { title: '小区信息' }, 64 group: { title: '小区信息' },
61 list: [ 65 list: [
62 - { type: 'el-input', label: '小区名', key: 'areaName', span: 12 },  
63 - { type: 'el-input', label: '门牌号', key: 'homeNum', span: 12 }, 66 + { type: 'el-input', label: '小区名', key: 'areaName' },
  67 + { type: 'el-input', label: '门牌号', key: 'homeNum' },
64 { 68 {
65 group: { title: 'A栋' }, 69 group: { title: 'A栋' },
66 list: [ 70 list: [
67 { type: 'el-input-number', label: '人数', key: 'anumber', 71 { type: 'el-input-number', label: '人数', key: 'anumber',
  72 + props: { 'controls-position': 'right' },
68 on: { 73 on: {
69 change(value) { 74 change(value) {
70 console.log(value); 75 console.log(value);
@@ -94,6 +99,24 @@ export default { @@ -94,6 +99,24 @@ export default {
94 } 99 }
95 ], 100 ],
96 }, 101 },
  102 + { type: 'el-input', label: '身高', key: 'height', props: { type: 'textarea', min: 3 }, style: { width: '100%' }, span: 24 },
  103 + { type: 'el-input', label: '体重', key: 'weight' },
  104 + { type: 'el-input', label: '身高', key: 'height' },
  105 + { type: 'el-input', label: '体重', key: 'weight' },
  106 + { type: 'el-input', label: '身高', key: 'height' },
  107 + { type: 'el-input', label: '体重', key: 'weight' },
  108 + { type: 'el-input', label: '身高', key: 'height' },
  109 + { type: 'el-input', label: '体重', key: 'weight' },
  110 + { type: 'el-input', label: '身高', key: 'height' },
  111 + { type: 'el-input', label: '体重', key: 'weight' },
  112 + { type: 'el-input', label: '身高', key: 'height' },
  113 + { type: 'el-input', label: '体重', key: 'weight' },
  114 + { type: 'el-input', label: '身高', key: 'height' },
  115 + { type: 'el-input', label: '体重', key: 'weight' },
  116 + { type: 'el-input', label: '身高', key: 'height' },
  117 + { type: 'el-input', label: '体重', key: 'weight' },
  118 + { type: 'el-input', label: '身高', key: 'height' },
  119 + { type: 'el-input', label: '体重', key: 'weight' },
97 { type: 'el-input', label: '身高', key: 'height' }, 120 { type: 'el-input', label: '身高', key: 'height' },
98 { type: 'el-input', label: '体重', key: 'weight' }, 121 { type: 'el-input', label: '体重', key: 'weight' },
99 ] 122 ]
@@ -124,8 +147,8 @@ export default { @@ -124,8 +147,8 @@ export default {
124 }, 3000); 147 }, 3000);
125 }, 148 },
126 methods: { 149 methods: {
127 - onValidate(v) {  
128 - console.log(v); 150 + onValidate(isValidated, model) {
  151 + console.log(isValidated, model);
129 }, 152 },
130 handleGetValue() { 153 handleGetValue() {
131 this.$refs.form.validate(); 154 this.$refs.form.validate();