需求:根据后台返回的数据,来生成一个form表单。几乎所有元素都是后台决定。后台会告诉前端这个页面里有几个input,label,select,selectDateTime等,以及他们的顺序。

封装组件,方便使用

field和actionsheet封装selectPicker等组件

我所封装的selectPicker用了很多插槽来满足样式需求,看起来有些繁复。而且这不是重点,就不列举代码了。仅展示一下所要达到的效果(封装以后的组件使用):

<select-picker 
	isRequired=true 
	fieldKey="influence" 
	fieldLabel="影响程度" 
	fieldValue="" 
	columns="['高', '中', '低']" 
	leftIcon="#icon_yxcd"
	@confirmSelect="confirmSelect"/> 

对于上述属性的说明
isRequired: 是否必填
fieldKey: name属性
fieldLabel: 显示的标题
fieldValue: 所选的值
columns: 选项
confirmSelect: 选好后调用的函数
leftIcon: 左边图标

以上只是selectPicker的组件封装。
类似的还封装了 类似textarea, dateTimePicker等组件。

利用Render函数来渲染

参考文章: 【Vue】动态添加组件的两种实现.
几乎就是参考这篇文章的第二种方式来实现的。
有关render,具体参考官网:: 渲染函数 & JSX.
就直接上代码了:
Test.vue

<template>
	<div class="test>
		<van-form>
			<template #default>
			<div ref="mainList"></div>
			</template>
		</van-form>	
	</div>
</template>

<script>
import FakeData from './utils/fakeDatas'
import RenderTags from './utils/renderTags'

export default {
	data() {
		return {
			list: []
		}
	}
	mounted() {
		this.list = this.getFakeListData();
		this.initTags(this.list);
	},
	methods: {
		getFakeListData() {
			//eg: { type: 'selectpicker', icon: 'icon_yingxiangchengdu', key: 'influence', keyValue: '影响程度', actions: ['高', '中', '低'], value: '', require: true },
			return FakeData.getList();
		},
		initTags(list) {
	      if(list.length > 0) {
	        for(let item of list) {
	          let tag;
	          switch(item.type) {
	            case 'selectpicker':
	              tag = RenderTags.renderSelectPickerTag(item);
	              break;
	            case 'datetime':
	              tag = RenderTags.renderDateTimePickerTag(item);
	              break;
	            case 'textarea':
	              tag = RenderTags.renderTextareaTag(item);
	              break;
	          }
	          if(tag) {
	            tag.$mount();
	            this.$refs.mainList.appendChild(tag.$el);
	          }
	        }
	      }
	    },
	}
}
</script>

renderTags.js 部分代码

renderSelectPickerTag(infoObj) {
    let selectPickerTag = Vue.extend({
      components: {
        SelectPicker
      },
      data() {
        return {
          value: infoObj.value || ''
        }
      },
      render(createElement) {
        return createElement('div', 
          {
            style: { 'margin-top': '4px' }
          },
          [
            createElement('SelectPicker',{
              attrs: {
                isRequired: infoObj.require,
                fieldKey: infoObj.key,
                fieldLabel: infoObj.keyValue,
                fieldValue: this.value,
                columns:  infoObj.actions,
                leftIcon: '#' + infoObj.icon, 
              },
              on: {
                'confirmSelect': this.onConfirmSelect
              }
            })
          ]
        );
      },
      methods: {
        onConfirmSelect(value) {
          this.value = value;
        }
      }
    });
    return new selectPickerTag();
  },

还有datetimepicker组件和textarea组件的方法就不全部列举了。
这种方法,利用ref找到目标div,然后进行替换。

测试之后可以实现。
但是有一个问题,由于我同时使用了vant库的form组件,
render出来的list没有办法和form绑定上。

原因是,form标签在渲染的时候,还没有render出来的list,因此无法进行绑定。

所以,我决定摒弃这种方法,改用下面的方式来完成。
利用is属性来完成,代码更加简便也更加容易理解。

利用动态组件的属性is来渲染

<template>
	<div class="test>
		<van-form>
			<template #default>
				<div class="main-list">
	          <div v-for="(item, index) in list" :key="index" class="itemClass">
	            <component :is="item.type" 
	              :isRequired="item.require"
	              :dateType="dateType"
	              :fieldKey="item.key"
	              :leftIcon="`#${item.icon}`"
	              :fieldValue="item.value"
	              :fieldLabel="item.keyValue"
	              :columns="item.actions"
	              @fieldConfirm="onConfirm($event ,index)" />
	          </div>
	        </div>
			</template>
		</van-form>	
	</div>
</template>

list便是后台返回来的数据了。

Logo

前往低代码交流专区

更多推荐