概要
由于开发工作需求中,很多的列表页都会有很多查询筛选条件的备选,但是其实查询筛选过程只需要选填查询筛选条件,不需要全部填,这种一大堆的条件选择及输入组件全部堆到一起。导致重要常用的几个筛选条件很难被找到,为了突出重点,结合大家的使用情况,可以将不常用的筛选条件折叠起来,需要更多筛选条件查询的时候再展开使用。
整体架构流程
当前这个可伸展的条件选择组件,主要是基于Ant Design of Vue组件库封装的。由于都是一些表单组件,所以外层主要使用的也就是antd的FormModel 表单组件,表单元素的布局我使用的是Grid 栅格布局。
技术细节
整体代码
<!--
* @description GFlexibleConditionsForm.vue 可伸缩的条件填写表单
* @author JUN - 2023/8/11 9:20
-->
<template>
<div :class="advanced ? 'search' : null">
<a-form-model ref="flexibleConditionsForm" :model="formData" v-bind="$attrs">
<!--展开显示-->
<div v-if="advanced">
<slot name="extend" v-bind="{formData}">
<!--为每一个formItem动态添加一个slot插槽,方便布局和控制(插槽的名称例如:“item_2_3”,表示第二行第三列的formItem)-->
<template v-for="rowIndex in rowCount">
<a-row :key="rowIndex">
<template v-for="colIndex in colCount">
<a-col :key="colIndex" v-bind="{...colOptions}">
<slot :name="`item_${rowIndex}_${colIndex}`" v-bind="{rowIndex,colIndex,advanced}"></slot>
</a-col>
</template>
<a-col></a-col>
</a-row>
</template>
</slot>
</div>
<!--收起显示-->
<div v-else class="fold">
<slot name="shrink" v-bind="{formData}">
<!--为每一个formItem动态添加一个slot插槽,方便布局和控制(插槽的名称例如:“item_2_3”,表示第二行第三列的formItem)-->
<template v-for="rowIndex in rowCount">
<a-row :key="rowIndex">
<template v-for="colIndex in colCount">
<a-col :key="colIndex" v-bind="{...colOptions}">
<slot :name="`item_${rowIndex}_${colIndex}_retain`" v-bind="{rowIndex,colIndex,advanced}"></slot>
</a-col>
</template>
<a-col></a-col>
</a-row>
</template>
</slot>
</div>
<slot v-bind="{formData}"></slot>
<slot name="operator" v-if="operator+''!=='null'">
<div style="float: right; margin-top: 3px;">
<a-button :type="okType" @click.prevent="onSubmitForm()">{{okText}}</a-button>
<a-button style="margin-left: 8px" @click.prevent="onResetForm()">{{cancelText}}</a-button>
<a @click="onToggleAdvanced()" style="margin-left: 8px">
{{ advanced ? '收起' : '展开' }}
<a-icon :type="advanced ? 'up' : 'down'"/>
</a>
</div>
</slot>
</a-form-model>
</div>
</template>
<script lang="ts">
import {
Vue, Component, Prop, Emit, Model,
} from 'vue-property-decorator';
type colCountType = 1 | 2 | 3 | 4 | 6;
@Component({ name: 'GFlexibleConditionsForm' })
export default class GFlexibleConditionsForm extends Vue {
/* 表单数据 */
@Model('change', {
type: Object,
default: () => ({})
}) formData: Record<string, any> | undefined;
/* 初始化是否展开 */
@Prop({
type: Boolean,
default: false
}) isExtend: boolean | undefined;
/* 行数(为了动态添加slot插槽使用) */
@Prop({
type: Number,
default: 1
}) rowCount: number | undefined;
/* 列数(为了动态添加slot插槽使用) */
@Prop({
type: Number,
default: 3
}) colCount: colCountType;
/* 确认按钮文字 */
@Prop({
type: String,
default: '查询'
}) okText: string | undefined;
/* 确认按钮类型 */
@Prop({
type: String,
default: 'primary'
}) okType: string | undefined;
/* 取消/重置按钮文字 */
@Prop({
type: String,
default: '重置'
}) cancelText: string | undefined;
/* 操作表单按钮部分,当不需要默认底部按钮时,可以设为 :operator="null" */
@Prop({
type: String,
default: ''
}) operator: string | undefined;
/* 展开更多 */
advanced = false;
/* a-col的设置参数 */
get colOptions() {
return {
md: 24 / this.colCount, // 根据列数动态变化
sm: 24,
};
}
/**
* @description 伸缩form事件处理
* @return void
* @author JUN - 2023/8/11 9:43
*/
onToggleAdvanced() {
this.advanced = !this.advanced;
}
/**
* @description 提交表单操作
* @return void
* @author JUN - 2023/8/11 11:40
*/
onSubmitForm() {
const { flexibleConditionsForm }: any = this.$refs;
if (flexibleConditionsForm) {
flexibleConditionsForm.validate((valid) => {
if (valid) {
this.emitSubmit({
formDom: flexibleConditionsForm,
value: this.formData,
});
}
});
}
}
/**
* @description 重置表单内容操作
* @return void
* @author JUN - 2023/8/11 11:41
*/
onResetForm() {
const { flexibleConditionsForm }: any = this.$refs;
if (flexibleConditionsForm) {
flexibleConditionsForm.resetFields();
this.emitReset(flexibleConditionsForm);
}
}
created() {
this.advanced = this.isExtend;
}
/**
* @description 表单提交的emit
* @return void
* @author JUN - 2023/8/11 11:43
*/
@Emit('submit')
emitSubmit(value) {
return value;
}
/**
* @description 表单重置的emit
* @return void
* @author JUN - 2023/8/11 11:43
*/
@Emit('reset')
emitReset(value) {
return value;
}
}
</script>
<style scoped lang="less">
.search {
margin-bottom: 54px;
}
.fold {
width: calc(100% - 216px);
display: inline-block
}
@media screen and (max-width: 900px) {
.fold {
width: 100%;
}
}
</style>
参数
成员 | 说明 | 类型 | 默认值 |
---|---|---|---|
formData(v-model) | 表单数据 | Object | {} |
isExtend | 初始化是否展开 | Boolean | false |
rowCount | 行数(为了动态添加slot插槽使用) | Number | 1 |
colCount | 列数(为了动态添加slot插槽使用),可选 1 | 2 | 3 | 4 | 6 | Number | 3 |
okText | 确认按钮文字 | String | "查询" |
okType | 确认按钮类型 | String | "primary" |
cancelText | 取消/重置按钮文字 | String | "重置" |
operator | 操作表单按钮部分,当不需要默认底部按钮时,可以设为 :operator="null" | String | "" |
shrink(方式一) | 条件收起的时候要显示的内容展示slot插槽 | Slot | - |
extend(方式一) | 条件展开的时候要显示的内容展示slot插槽 | Slot | - |
item_{rowIndex}_${colIndex} (方式二) | 根据rowCount和colCount动态生成的slot插槽,插槽的名称如:item_1_2表示的是第一行的第二列的slot(收缩状态) | Slot | - |
item_{rowIndex}_${colIndex}_retain (方式二) | 根据rowCount和colCount动态生成的slot插槽,插槽的名称如:item_1_2表示的是第一行的第二列的slot(展开状态) | Slot | - |
isStrictMode | 动态生成表单组件slot插槽的模式选择,是否是严格模式,如果为false就不会有item_{rowIndex}_${colIndex}_retain类的插槽了 | Boolean | true |
事件
事件名称 | 说明 | 回调参数 |
---|---|---|
submit | 提交表单时回调,参数中formDom为当前组件表单的dom对象,formData为表单数据 | function(formDom,formData) |
reset | 重置表单时回调,参数中formData为表单数据 | function(formDom) |
使用
方式一
上面的参数说明里面的 shrink 和 extend 参数就是控制条件表单的伸缩显示的插槽
代码
<!--
* @description GFlexibleConditionsFormDemo.vue 可伸缩的条件填写表单Demo(方式一)
* @author JUN - 2023/8/11 9:21
-->
<template>
<g-flexible-conditions-form :row-count="2" :col-count="3" v-model="formData" @submit="onSubmit" @reset="onReset">
<a-form-model-item label="规则编号"
slot="shrink"
prop="num"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-input placeholder="请输入" v-model="formData.num"/>
</a-form-model-item>
<a-form-model-item label="使用状态"
slot="extend"
prop="status"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-select placeholder="请选择" v-model="formData.status">
<a-select-option value="1">关闭</a-select-option>
<a-select-option value="2">运行中</a-select-option>
</a-select>
</a-form-model-item>
</g-flexible-conditions-form>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import GFlexibleConditionsForm from '@/components/common/forms/GFlexibleConditionsForm.vue';
@Component({
name: 'GFlexibleConditionsFormDemo',
components: { GFlexibleConditionsForm }
})
export default class GFlexibleConditionsFormDemo extends Vue {
formData = {
num: 12,
status: undefined,
callCount: 0,
updateDate: undefined,
createdDate: undefined,
desc: undefined,
};
onSubmit({
formDom,
value,
}) {
alert(JSON.stringify(value));
}
onReset(formDom) {
debugger;
}
}
</script>
<style scoped lang="less">
</style>
![](https://i-blog.csdnimg.cn/blog_migrate/1364059d7ab4bd420115dd5bf5cf6c98.png)
![](https://i-blog.csdnimg.cn/blog_migrate/01fc784110cf706c9c8d3c48ef466c86.png)
优点
表单元素添加比较灵活
缺点
需要自己布局表单元素
方式二
也是看上面的参数说明,可以根据rowCount和colCount动态生成的slot插槽(如:item_1_2),将筛选条件组件放入插槽中显示
代码
<!--
* @description GFlexibleConditionsFormDemo.vue 可伸缩的条件填写表单Demo(方式二)
* @author JUN - 2023/8/11 9:21
-->
<template>
<g-flexible-conditions-form :row-count="2" :col-count="3" v-model="formData" @submit="onSubmit" @reset="onReset">
<a-form-model-item label="规则编号"
slot="item_1_1"
prop="num"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-input placeholder="请输入" v-model="formData.num"/>
</a-form-model-item>
<a-form-model-item label="使用状态"
slot="item_1_2"
prop="status"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-select placeholder="请选择" v-model="formData.status">
<a-select-option value="1">关闭</a-select-option>
<a-select-option value="2">运行中</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="调用次数"
slot="item_1_3"
prop="callCount"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-input-number style="width: 100%" placeholder="请输入" v-model="formData.callCount"/>
</a-form-model-item>
<a-form-model-item label="更新日期"
slot="item_2_1"
prop="updateDate"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-date-picker style="width: 100%" placeholder="请输入更新日期" v-model="formData.updateDate"/>
</a-form-model-item>
<a-form-model-item label="创建日期"
slot="item_2_2"
prop="createdDate"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-date-picker style="width: 100%" placeholder="请输入创建日期" v-model="formData.createdDate"/>
</a-form-model-item>
<a-form-model-item label="描述"
slot="item_2_3"
prop="desc"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-input placeholder="请输入" v-model="formData.desc"/>
</a-form-model-item>
<a-form-model-item label="使用状态_保留"
slot="item_1_2_retain"
prop="status"
:labelCol="{span: 6}"
:wrapperCol="{span: 17, offset: 1}">
<a-select placeholder="请选择" v-model="formData.status">
<a-select-option value="1">关闭</a-select-option>
<a-select-option value="2">运行中</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="更新日期_保留"
slot="item_1_1_retain"
prop="updateDate"
:labelCol="{span: 6}"
:wrapperCol="{span: 17, offset: 1}">
<a-date-picker style="width: 100%" placeholder="请输入更新日期" v-model="formData.updateDate"/>
</a-form-model-item>
<a-form-model-item label="规则编号_保留"
slot="item_1_3_retain"
prop="num"
:labelCol="{span: 6}"
:wrapperCol="{span: 17, offset: 1}">
<a-input placeholder="请输入" v-model="formData.num"/>
</a-form-model-item>
</g-flexible-conditions-form>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import GFlexibleConditionsForm from '@/components/common/forms/GFlexibleConditionsForm.vue';
@Component({
name: 'GFlexibleConditionsFormDemo',
components: { GFlexibleConditionsForm }
})
export default class GFlexibleConditionsFormDemo extends Vue {
formData = {
num: 12,
status: undefined,
callCount: 0,
updateDate: undefined,
createdDate: undefined,
desc: undefined,
};
onSubmit({
formDom,
value,
}) {
alert(JSON.stringify(value));
}
onReset(formDom) {
debugger;
}
}
</script>
<style scoped lang="less">
</style>
![](https://i-blog.csdnimg.cn/blog_migrate/822ae31593ebd1a10951603abd7c95ce.png)
![](https://i-blog.csdnimg.cn/blog_migrate/79c738a1d02e13139fe72fe3c3c9f136.png)
优点
布局不用自己操作,只要采用填空的方式(slot:item_{rowIndex}_{colIndex})插入表单组件就行,使用也相对灵
缺点
由于插槽太多,所以需要仔细插入表单组件
方式三
这种方式的使用有个前提,就是条件收缩以后要显示的表单组件,需要连续插入,否则收起的时候组件之间会有空格,而且这个需要使用方式二中的动态插槽,根方式不同的就是收起展开时判断方式的不同。isStrictMode参数必须设置为false,否则无法使用方式三
代码
<!--
* @description GFlexibleConditionsFormDemo.vue 可伸缩的条件填写表单Demo(方式三)
* @author JUN - 2023/8/11 9:21
-->
<template>
<g-flexible-conditions-form :row-count="2" :is-strict-mode="false" :col-count="3" v-model="formData" @submit="onSubmit" @reset="onReset">
<a-form-model-item label="规则编号"
slot="item_1_1"
prop="num"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-input placeholder="请输入" v-model="formData.num"/>
</a-form-model-item>
<a-form-model-item label="使用状态"
slot="item_1_2"
prop="status"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-select placeholder="请选择" v-model="formData.status">
<a-select-option value="1">关闭</a-select-option>
<a-select-option value="2">运行中</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="调用次数"
slot="item_1_3"
prop="callCount"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-input-number style="width: 100%" placeholder="请输入" v-model="formData.callCount"/>
</a-form-model-item>
<a-form-model-item label="更新日期"
slot="item_2_1"
slot-scope="{advanced}"
v-if="advanced"
prop="updateDate"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-date-picker style="width: 100%" placeholder="请输入更新日期" v-model="formData.updateDate"/>
</a-form-model-item>
<a-form-model-item label="创建日期"
slot="item_2_2"
slot-scope="{advanced}"
v-if="advanced"
prop="createdDate"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-date-picker style="width: 100%" placeholder="请输入创建日期" v-model="formData.createdDate"/>
</a-form-model-item>
<a-form-model-item label="描述"
slot="item_2_3"
slot-scope="{advanced}"
v-if="advanced"
prop="desc"
:labelCol="{span: 5}"
:wrapperCol="{span: 18, offset: 1}">
<a-input placeholder="请输入" v-model="formData.desc"/>
</a-form-model-item>
</g-flexible-conditions-form>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import GFlexibleConditionsForm from '@/components/common/forms/GFlexibleConditionsForm.vue';
@Component({
name: 'GFlexibleConditionsFormDemo',
components: { GFlexibleConditionsForm }
})
export default class GFlexibleConditionsFormDemo extends Vue {
formData = {
num: 12,
status: undefined,
callCount: 0,
updateDate: undefined,
createdDate: undefined,
desc: undefined,
};
onSubmit({
formDom,
value,
}) {
alert(JSON.stringify(value));
}
onReset(formDom) {
debugger;
}
}
</script>
<style scoped lang="less">
</style>
![](https://i-blog.csdnimg.cn/blog_migrate/20e5427027651c66ef4295110e247698.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9fcafe2ef379e30142ed96affddd381d.png)
其实第三种方式使用的是slot-scope(插槽作用域)带有的是否已经展开的表示参数advanced ,作为判断参数进行表单组件的显示与隐藏
小结
其实还有其他的使用方式,可以灵活使用参数配置,解决开发需求。