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 1 <template>
2 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 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 31 </el-form-item>
20   - </el-col>
  32 + </component>
21 33 </template>
22   - </el-row>
  34 + </component>
23 35 </template>
24 36  
25 37 <script>
... ... @@ -32,8 +44,18 @@ export default {
32 44 itemKey: String,
33 45 titleClass: String,
34 46 contentClass: String,
  47 + itemClass: String,
  48 + type: String,
35 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 59 methods: {
38 60 /**
39 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 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 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 31 {{ list }}
7 32 </el-form>
8 33 </template>
... ... @@ -20,6 +45,10 @@ export default {
20 45 formClass: String,
21 46 titleClass: String,
22 47 contentClass: String,
  48 + itemClass: String,
  49 + labelWidth: String,
  50 + labelPosition: String,
  51 + type: String,
23 52 span: {
24 53 type: Number,
25 54 default: 24
... ... @@ -85,7 +114,7 @@ export default {
85 114 */
86 115 validate() {
87 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 141 * @description 手动更新某一表单项的值
113 142 * @param {Object} param 需要更新的参数对象或者对象数组 { name => 表单项key,可嵌套; value => 更新的值 }
114 143 * @example { name: 'a.b.c', value: 123 }
  144 + * @example { name: 'd.0.e', value: ['f'] }
115 145 */
116 146 onItemUpdate(param) {
117 147 this.$nextTick(() => {
... ...
examples/views/page/form-new/util.js
  1 +/**
  2 + * 深度克隆对象
  3 + * @param {Object} obj 目标对象
  4 + * @returns {Object} 克隆的新对象
  5 + */
1 6 export const cloneDeep = (obj) => {
2 7 if (typeof obj !== 'object') {
3 8 return obj;
... ... @@ -35,19 +40,31 @@ export const cloneDeep = (obj) =&gt; {
35 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 50 export const get = (obj, dotSeparatedKeys) => {
39 51 if (dotSeparatedKeys !== undefined && typeof dotSeparatedKeys !== 'string') return undefined;
40 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 54 const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== '');
44   -
45   - // eslint-disable-next-line no-param-reassign, no-confusing-arrow
46 55 obj = pathArr.reduce((o, key) => (o && o[key] !== undefined ? o[key] : undefined), obj);
47 56 }
48 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 68 export const set = (obj, dotSeparatedKeys, value) => {
52 69 const splitRegex = /[.\[\]'"]/g;
53 70 const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== '');
... ... @@ -58,7 +75,6 @@ export const set = (obj, dotSeparatedKeys, value) =&gt; {
58 75 }
59 76 return o[k]
60 77 }, obj)[key] = value
61   - // pathArr.reduce((o, key) => (o && o[key] !== 'undefined' ? o[key] : undefined), obj)[key] = value
62 78 };
63 79  
64 80 export default {
... ...
examples/views/page/other.vue
... ... @@ -15,7 +15,11 @@
15 15 <p>这是一个非markdown页面</p>
16 16 <pre>{{ model }}</pre>
17 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 23 </div>
20 24 </template>
21 25  
... ... @@ -36,14 +40,14 @@ export default {
36 40 option: {
37 41 list: [
38 42 {
39   - group: { title: '基础信息' },
  43 + group: { title: '基础信息', span: 12 },
40 44 list: [
41 45 { type: 'el-input', label: '名称', key: 'name' },
42 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 53 group: { title: '住址', key: 'location', span: 24 },
... ... @@ -52,19 +56,20 @@ export default {
52 56 {
53 57 group: { key: 'district' },
54 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 64 group: { title: '小区信息' },
61 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 69 group: { title: 'A栋' },
66 70 list: [
67 71 { type: 'el-input-number', label: '人数', key: 'anumber',
  72 + props: { 'controls-position': 'right' },
68 73 on: {
69 74 change(value) {
70 75 console.log(value);
... ... @@ -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 120 { type: 'el-input', label: '身高', key: 'height' },
98 121 { type: 'el-input', label: '体重', key: 'weight' },
99 122 ]
... ... @@ -124,8 +147,8 @@ export default {
124 147 }, 3000);
125 148 },
126 149 methods: {
127   - onValidate(v) {
128   - console.log(v);
  150 + onValidate(isValidated, model) {
  151 + console.log(isValidated, model);
129 152 },
130 153 handleGetValue() {
131 154 this.$refs.form.validate();
... ...