Commit 2db4f03a6f5111d97fa7f3304fbb635b0137f63c

Authored by Aaron
1 parent fd68ba0b
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;" :class="contentClass || 'eagle-form__group-content'">
  3 + <el-row type="flex" style="flex-wrap: wrap;width: 100%;" :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 || 24">
7   - <el-row type="flex" style="flex-wrap: wrap;">
  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%;">
8 8 <!-- 表单分组标题 -->
9 9 <el-col :class="titleClass || 'eagle-form__group-title'" v-if="item.group.title" :span="24">{{ item.group.title || item.group }}</el-col>
10 10 <!-- 递归本组件 -->
... ... @@ -12,7 +12,7 @@
12 12 </el-row>
13 13 </el-col>
14 14 <!-- 正常无分组表单项 -->
15   - <el-col v-else :span="item.span || 12" :key="index">
  15 + <el-col v-else :span="item.span || span" :key="index">
16 16 <el-form-item :label="item.label" :label-width="item.labelWidth || '120px'" :prop="item.fullKey" :rules="item.rules">
17 17 <!-- 自定义组件 -->
18 18 <component :is="item.type" :value="itemValue(item)" @input="v => onInput({ value: v, item })" v-on="bindItemEvent(item)"></component>
... ... @@ -32,6 +32,7 @@ export default {
32 32 itemKey: String,
33 33 titleClass: String,
34 34 contentClass: String,
  35 + span: Number,
35 36 },
36 37 methods: {
37 38 /**
... ... @@ -92,7 +93,7 @@ export default {
92 93 bindItemEvent(item) {
93 94 if (item.on) {
94 95 if (typeof item.on === 'function') {
95   - return item.on({ model: this.value, update: ({ name, value }) => this.$emit('item-update', { name, value }) });
  96 + return item.on({ model: this.value, update: e => this.$emit('item-update', e) });
96 97 } else {
97 98 return item.on
98 99 }
... ...
examples/views/page/form-new/index.vue
1 1 <template>
2 2 <el-form ref="form" size="small" :class="formClass" :model="formModel">
3 3 {{ formModel }}
4   - <form-render :title-class="titleClass" :content-class="contentClass" :list="formList" :value="model" :model="model" @item-change="onItemChange" @form-item-change="onFormItemChange" @item-update="onItemUpdate"></form-render>
  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>
5 6 {{ list }}
6 7 </el-form>
7 8 </template>
8 9  
9 10 <script>
10 11 import FormRender from './form-render';
11   -import { cloneDeep, set } from 'lodash';
  12 +import { cloneDeep, set } from './util';
12 13  
13 14 export default {
14 15 name: 'FormNew',
... ... @@ -19,40 +20,49 @@ export default {
19 20 formClass: String,
20 21 titleClass: String,
21 22 contentClass: String,
  23 + span: {
  24 + type: Number,
  25 + default: 24
  26 + }
22 27 },
23 28 data() {
24 29 return {
25 30 model: {},
26 31 formModel: {},
  32 + formList: []
27 33 }
28 34 },
29 35 watch: {
30   - value(val = {}) {
31   - this.model = val;
32   - this.setFormModel(val)
  36 + value: {
  37 + handler(val = {}) {
  38 + this.model = val;
  39 + this.setFormModel(val)
  40 + },
  41 + immediate: true
33 42 },
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}`;
  43 + list: {
  44 + handler(val) {
  45 + // 深度克隆传入的列表,避免原始值被修改
  46 + const newList = cloneDeep(this.list);
  47 + // 生成列表值的全路径key,即列表项为对象时,对象内的key与上一级的key合并作为全路径key
  48 + const generateFullKey = (list, parentKey) => {
  49 + list.forEach(item => {
  50 + if (item.group && item.list) {
  51 + if (item.group.key) {
  52 + item.fullKey = `${parentKey ? `${parentKey}-${item.group.key}` : item.group.key}`;
  53 + } else {
  54 + item.fullKey = item.key;
  55 + }
  56 + generateFullKey(item.list, item.fullKey);
45 57 } else {
46   - item.fullKey = item.key;
  58 + item.fullKey = `${parentKey ? `${parentKey}-${item.key}` : item.key}`;
47 59 }
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;
  60 + });
  61 + };
  62 + generateFullKey(newList);
  63 + this.formList = newList;
  64 + },
  65 + immediate: true
56 66 }
57 67 },
58 68 methods: {
... ... @@ -100,13 +110,17 @@ export default {
100 110 },
101 111 /**
102 112 * @description 手动更新某一表单项的值
103   - * @param {Object} { name => 表单项key,可嵌套; value => 更新的值 }
  113 + * @param {Object} param 需要更新的参数对象或者对象数组 { name => 表单项key,可嵌套; value => 更新的值 }
104 114 * @example { name: 'a.b.c', value: 123 }
105 115 */
106   - onItemUpdate({ name, value }) {
107   - const newModel = cloneDeep(this.model);
108   - set(newModel, name, value);
109   - this.$emit('input', newModel);
  116 + onItemUpdate(param) {
  117 + this.$nextTick(() => {
  118 + const newModel = cloneDeep(this.model);
  119 + Object.entries(param).forEach(entry => {
  120 + set(newModel, entry[0], entry[1]);
  121 + });
  122 + this.$emit('input', newModel);
  123 + });
110 124 }
111 125 }
112 126 }
... ...
examples/views/page/form-new/util.js 0 → 100644
... ... @@ -0,0 +1,68 @@
  1 +export const cloneDeep = (obj) => {
  2 + if (typeof obj !== 'object') {
  3 + return obj;
  4 + }
  5 + if (!obj) {
  6 + return obj;
  7 + }
  8 + if (obj instanceof Date) {
  9 + return new Date(obj);
  10 + }
  11 + if (obj instanceof RegExp) {
  12 + return new RegExp(obj);
  13 + }
  14 + if (obj instanceof Function) {
  15 + return obj;
  16 + }
  17 + let newObj;
  18 + if (obj instanceof Array) {
  19 + newObj = [];
  20 + for(let i = 0, len = obj.length; i < len; i++){
  21 + newObj.push(cloneDeep(obj[i]));
  22 + }
  23 + return newObj;
  24 + }
  25 + newObj = {};
  26 + for(let key in obj) {
  27 + if (obj.hasOwnProperty(key)) {
  28 + if (typeof obj[key] !== 'object') {
  29 + newObj[key] = obj[key];
  30 + } else {
  31 + newObj[key] = cloneDeep(obj[key]);
  32 + }
  33 + }
  34 + }
  35 + return newObj;
  36 +};
  37 +
  38 +export const get = (obj, dotSeparatedKeys) => {
  39 + if (dotSeparatedKeys !== undefined && typeof dotSeparatedKeys !== 'string') return undefined;
  40 + 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
  43 + 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);
  47 + }
  48 + return obj;
  49 +};
  50 +
  51 +export const set = (obj, dotSeparatedKeys, value) => {
  52 + const splitRegex = /[.\[\]'"]/g;
  53 + const pathArr = dotSeparatedKeys.split(splitRegex).filter(k => k !== '');
  54 + const key = pathArr.pop();
  55 + pathArr.reduce((o, k) => {
  56 + if (o && o[k] === undefined || o[k] === null) {
  57 + o[k] = !isNaN(Number(key)) ? [] : {}
  58 + }
  59 + return o[k]
  60 + }, obj)[key] = value
  61 + // pathArr.reduce((o, key) => (o && o[key] !== 'undefined' ? o[key] : undefined), obj)[key] = value
  62 +};
  63 +
  64 +export default {
  65 + cloneDeep,
  66 + get,
  67 + set
  68 +}
0 69 \ No newline at end of file
... ...
examples/views/page/other.vue
... ... @@ -15,7 +15,7 @@
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" 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" :span="12" form-class="custom-form" title-class="custom-title" content-class="custom-content"></eg-form>
19 19 </div>
20 20 </template>
21 21  
... ... @@ -27,36 +27,44 @@ export default {
27 27 components: { EgForm },
28 28 data() {
29 29 return {
30   - model: {},
  30 + model: {
  31 + name: 'name',
  32 + location: {
  33 + areaName: 'hahhhahaa',
  34 + },
  35 + },
31 36 option: {
32 37 list: [
33 38 {
34   - group: { title: '基础信息', span: 12 },
  39 + group: { title: '基础信息' },
35 40 list: [
36 41 { type: 'el-input', label: '名称', key: 'name' },
37 42 { type: 'el-input-number', label: '年龄', key: 'age' },
38 43 ],
39 44 },
40 45 {
41   - group: { title: '住址', span: 24, key: 'location' },
  46 + group: { title: '' },
  47 + },
  48 + {
  49 + group: { title: '住址', key: 'location', span: 24 },
42 50 list: [
43 51 { type: 'el-input', label: '地址简称', key: 'locationMin' },
44 52 {
45   - group: { span: 12, key: 'district' },
  53 + group: { key: 'district' },
46 54 list: [
47   - { type: 'el-input', label: '省', key: 'province', span: 24, rules: [{ required: true, message: '请输入省' }] },
48   - { type: 'el-input', label: '市', key: 'city', span: 24 },
  55 + { type: 'el-input', label: '省', key: 'province', rules: [{ required: true, message: '请输入省' }], span: 12 },
  56 + { type: 'el-input', label: '市', key: 'city', span: 12 },
49 57 ],
50 58 },
51 59 {
52   - group: { title: '小区信息', span: 24 },
  60 + group: { title: '小区信息' },
53 61 list: [
54   - { type: 'el-input', label: '小区名', key: 'areaName', span: 24 },
55   - { type: 'el-input', label: '门牌号', key: 'homeNum', span: 24 },
  62 + { type: 'el-input', label: '小区名', key: 'areaName', span: 12 },
  63 + { type: 'el-input', label: '门牌号', key: 'homeNum', span: 12 },
56 64 {
57   - group: { title: 'A栋', span: 24 },
  65 + group: { title: 'A栋' },
58 66 list: [
59   - { type: 'el-input-number', label: '人数', key: 'anumber', span: 24,
  67 + { type: 'el-input-number', label: '人数', key: 'anumber',
60 68 on: {
61 69 change(value) {
62 70 console.log(value);
... ... @@ -66,15 +74,15 @@ export default {
66 74 ],
67 75 },
68 76 {
69   - group: { title: 'B栋', span: 24, key: 'bside' },
  77 + group: { title: 'B栋', key: 'bside' },
70 78 list: [
71   - { type: 'el-input-number', label: '人数', key: 'bnumber', span: 24,
  79 + { type: 'el-input-number', label: '人数', key: 'bnumber',
72 80 on({ model, update }) {
73 81 return {
74 82 change(value) {
75 83 if (value === 18 && model.age === 18) {
76   - // TODO update后本值不变的BUG
77   - update({ name: 'location.areaName', value: 'haha' });
  84 + console.log(model)
  85 + update({ 'location.areaName': 'hehe', name: 'aaa', 'text.0': 'abc', 'ttt.0': 'abc' });
78 86 }
79 87 }
80 88 }
... ...