form-item组件封装细讲中(组件库系列~五)

前言

在之前的文章中,我们介绍了form-item组件的dom结构处理以及异常情况slot的插入处理。今天,我们将要聊一聊关于form-item组件布局实现的方法和需要注意的事项。

单组件在实际开发中的重要性

在实际开发中,表单组件是非常重要的组件之一。特别是在后台系统应用中,表单组件的应用非常普遍,主要包括新增、编辑的提交表单以及查询表单。

不同展现形式的表单需要大量的样式布局开发时间,因此我们希望表单组件可以将这些功能全部整合到一起,通过简单的参数控制即可完成。这样可以提高开发效率,降低开发难度。

1. 一行多个的布局------解决方案

  1. 首先我们会在 form-item 遍历的最外层加上 flex 的换行样式,然后设置内部的每个遍历元素的宽度百分比。这里我们采用 col-24 的栅格布局。要知道每一个子组件占据的宽度,首先我们可以定义一行放多少列,比如说一行四列,那么每一个就是 col-8 (25%)。所以我们定一个入参来控制:
props: {
	form: { type: Object, required: true, default: () => {} }, // 传进来的共享的form表单值对象
	config: {type: Array, default: () => [
		// itemType值的可选项为可能出现的高频组件名
		// input\select\radio\checkbox\date-picker等(组件内部需要写清楚,且需要在require中添加入参强制校验)
		{itemType: "input", label: "姓名", prop: "name"},
		{itemType: "select", label: "性别", prop: "sex"},
		{itemType: "select", label: "身份证插槽", prop: "idCard", isSlot: true}
	]},
	column: { 
		type: [Number, String],
		default: 4,
		validator: value => [1, 2, 3, 4, 6].indexOf(Number(value)) !== -1 
	}
}

接下来我们还需要考虑到有些情况下,可能某一个元素需要占到更宽的宽度,比如说像下面图中现居住地户籍地址的字段一样:
在这里插入图片描述
这样的情况我们需要能够对单独的列进行配置,所以我们可以在config中添加额外参数span:

span参数:把column设置的参数为n等份。(column=“4”,那么一共为4等份)未设置span参数则默认为1等分,后面可以跟上不同的等份值

HTML:

<el-form-item 
	v-for="(item, index) in realConfig"  
	:key="'form-item' + index"  
	:class="(item.span && 'col-' + colVal * item.span) || `col-${colVal}`"
	:label="item.label" 
	:prop="item.prop">
	<components
		v-if="!item.isSlot"
	    :is="'item-' + item.itemType || 'text'"
	    :form="form"
	    v-bind="{ ...item, noLabel }"
	/>
	<slot v-else :name="item.prop" />
</el-form-item>

JS

props: {
	form: { type: Object, required: true, default: () => {} },
	config: {type: Array, default: () => [
		{itemType: "input", label: "姓名", prop: "name"},
		{itemType: "input", label: "不可见元素", prop: "name", hidden: true},
		// 添加了span值为2,如果等份为4,那么这个宽度占到50%,其他未设置的宽度为25%
		{itemType: "select", label: "性别", prop: "sex", span: 2},
		{itemType: "select", label: "身份证插槽", prop: "idCard", isSlot: true}
	]}// 每一行有几列
	column: { 
		type: [Number, String],
		default: 4,
		validator: value => [1, 2, 3, 4, 6].indexOf(Number(value)) !== -1 
	}
},
computed: {
	// 兼容处理column参数为String类型,虽然可以在props里面指定类型为Number
	// 但是Number传值会比String更麻烦
	// Number => :column="4" String => column="4"
    realColumn() {
        return Number(this.column) || 4;
    },
    // 整体列数对应的col
    colVal() {
        return 24 / this.realColumn;
    }// 可以看到html代码中遍历的是该参数,主要是用于支持config中可以添加hidden参数
   	// 动态的控制部分元素隐藏
    realConfig() {
        return this.config.filter(e => !e.hidden);
    },
}

2. 全局禁用和清除功能实现

这种功能就很简单了,直接通过props进行参数接收。然后通过components进行参数的传递。让各个子组件内部处理,不过需要以config内部参数为主,下面为代码实现:

HTML:

<el-form-item 
	v-for="(item, index) in realConfig"  
	:key="'form-item' + index"  
	:class="(item.span && 'col-' + colVal * item.span) || `col-${colVal}`"
	:label="item.label" 
	:prop="item.prop">
	<components
		v-if="!item.isSlot"
	    :is="'item-' + item.itemType || 'text'"
	    :form="form"
	    :disabled="item.disabled || disabled"
        :clearable="item.clearable || clearable"
	    v-bind="{ ...item }"
	/>
	<slot v-else :name="item.prop" />
</el-form-item>

JS

props: {
	disabled: { type: Boolean, default: false }, // 全局禁用
    clearable: { type: Boolean, default: false }, // 全组件带清除功能
},

3. 宽度设定

需要在props中添加一个width的入参,值默认为100%,该值为元素的宽度占对应的form-item子项宽度的值。(比如说column: 4,那么每一列为25%,该width值设定的是这个25%的宽度);

HTML:

<el-form-item 
	v-for="(item, index) in realConfig"  
	:key="'form-item' + index"  
	:class="(item.span && 'col-' + colVal * item.span) || `col-${colVal}`"
	:label="item.label" 
	:prop="item.prop">
	<components
		v-if="!item.isSlot"
	    :is="'item-' + item.itemType || 'text'"
	    :form="form"
	    :disabled="item.disabled || disabled"
        :clearable="item.clearable || clearable"
        :style="{ width: itemWidth(item, index) }"
	    v-bind="{ ...item }"
	/>
	<slot v-else :name="item.prop" />
</el-form-item>

JS

props: {
	config: {type: Array, default: () => [
		// 设置该项宽度为span:1所占宽度的80%
		{itemType: "input", label: "姓名", prop: "name", width: 80%},
		{itemType: "input", label: "不可见元素", prop: "name", hidden: true},
		// 添加了span值为2,如果等份为4,那么这个宽度占到50%,其他未设置的宽度为25%
		{itemType: "select", label: "性别", prop: "sex", span: 2},
		{itemType: "select", label: "身份证插槽", prop: "idCard", isSlot: true}
	]},
	width: { type: String, default: '100%' }, // 每一项form-item项表单内容宽度
},
computed: {
	itemWidth() {
     	return function (data, index) {
     		// 判定当前项是否为当前行最后一项,如果是那么需要减少各子组件中间距的大小
	       if (!this.lastIndex.includes(index)) {
	            return `calc(${data.width || this.width} - ${this.$Config.Components.formItem.space})`;
	        }
	        // 以config中的width参数为主
	        return data.width || this.width;
    	};
 	}
}

4. 默认插槽----按钮

在实际开发中遇到列表页面form表单查询的情况,时常需要控制按钮的位置。我们也可以通过组件层做一定的控制。

  1. 通过css样式控制按钮的宽度为当前行剩余宽度的100%。
  2. 因为操作按钮不存在label字段,所以设定该form-item的label-width宽度固定为0;
  3. 添加rightSide参数,默认为false,为true时设置justify-content: flex-end;等css属性。
    代码如下:

HTML:

<div class="flex-wrap">
	<el-form-item 
		v-for="(item, index) in realConfig"  
		:key="'form-item' + index"  
		:class="(item.span && 'col-' + colVal * item.span) || `col-${colVal}`"
		:label="item.label" 
		:prop="item.prop">
		<components
			v-if="!item.isSlot"
		    :is="'item-' + item.itemType || 'text'"
		    :form="form"
		    :disabled="item.disabled || disabled"
	        :clearable="item.clearable || clearable"
	        :style="{ width: itemWidth(item, index) }"
		    v-bind="{ ...item }"
		/>
		<slot v-else :name="item.prop" />
	</el-form-item>
	<el-form-item v-if="$slots.default" :class="{ formButtonRight: rightSide }" label-width="0px" class="flex-auto slot">
        <slot />
    </el-form-item>
</div>

JS

props: {
	rightSide: { type: Boolean, default: false }, // 查询按钮是否放在右侧
}

CSS

.formButtonRight {
	display: flex;
	justify-content: flex-end;
	.el-form-item__content {
		padding-right: 0;
	}
}

5. config中添加componets组件,支持自定义组件扩展

为了满足业务需求,有时我们需要在form-item组件中添加一些不同于input、select等子组件的公共组件,例如图片上传组件的二次封装等。为了实现这一需求,我们提供了一种方便的自定义组件嵌入方式——通过添加config参数components参数来实现。可以满足您的自定义需求。具体实现方法如下:

HTML

<div class="flex-wrap">
	<el-form-item 
		v-for="(item, index) in realConfig"  
		:key="'form-item' + index"  
		:class="(item.span && 'col-' + colVal * item.span) || `col-${colVal}`"
		:label="item.label" 
		:prop="item.prop">
		<components
			v-if="!item.isSlot && !item.components"
		    :is="'item-' + item.itemType || 'text'"
		    :form="form"
		    :disabled="item.disabled || disabled"
	        :clearable="item.clearable || clearable"
	        :style="{ width: itemWidth(item, index) }"
		    v-bind="{ ...item }"
		/>
		<!-- item.components如果有值,那么通过components + :is的方式进行组件的渲染。
		通过v-model="form[item.prop]"进行数据绑定
		并通过v-bind将config所有参数传递下去 -->
		<components v-bind="item" :is="item.components" v-else-if="item.components" v-model="form[item.prop]" />
        <slot v-else v-bind="item" :name="item.prop" />
	</el-form-item>
	<el-form-item v-if="$slots.default" :class="{ formButtonRight: rightSide }" label-width="0px" class="flex-auto slot">
        <slot />
    </el-form-item>
</div>

JS

props: {
	config: {type: Array, default: () => [
		// 设置该项宽度为span:1所占宽度的80%
		{itemType: "input", label: "姓名", prop: "name", width: 80%},
		{itemType: "input", label: "不可见元素", prop: "name", hidden: true},
		// 添加了span值为2,如果等份为4,那么这个宽度占到50%,其他未设置的宽度为25%
		{itemType: "select", label: "性别", prop: "sex", span: 2},
		// com-upload这个组件需要已经注册好,全局注册或者使用层当前vue文件注册都可以
		{components: 'com-upload', label: "自定义上传组件", prop: "uploadFile",  ext: ".jpg,.jpeg,.png,.gif"},
		{itemType: "select", label: "身份证插槽", prop: "idCard", isSlot: true}
	]}}

PS:下一遍文章会详细的讲一下form-item里面关于label-width的宽度宽度处理。

总结:这里我们就实现了form-item组件的基本开发,已经能在项目中进行使用了,关于内部的input、select等组件,大家可以通过gitee进行查看。hc-basic组件库已开源。

组件库代码地址

https://gitee.com/yangxiongasin/hc-basic

组件库文档代码地址

https://gitee.com/yangxiongasin/hc-docs

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端杨雄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值