做web端页面时,伴随列表的是各种各样的搜索条件,当各种页面搜索条件都比较多时,此组件将简化代码
组件1: 无更多查询按钮,一行显示所有搜索条件
<template>
<div class="searchArea">
<el-form
v-if="fieldColumns.length > 0"
ref="formRef"
:inline="inline"
:model="formData"
class="elForm"
v-bind="formOpt"
>
<template v-for="item in fieldColumns">
<component
:is="'el-form-item'"
v-if="!item.slot"
:label="item.label"
:prop="item.name"
v-bind="item.formItemOpt"
>
<component
:is="`el-${item.type}`"
v-model="formData[item.name]"
:collapse-tags="item.collapseTags"
:disabled="item.disabled || false"
:options="item.options|| ''"
:placeholder="item.placeholder"
:popper-append-to-body="true"
:props="item.props"
:style="{ width: item.width || '240px' }"
v-bind="item.opt"
@change="handleChange"
>
<i v-if="item.type === 'input' && item.search" slot="suffix"
class="el-icon-search cursor-pointer" @click="handleChange"></i>
<template v-if="item.type === 'select'">
<el-option
v-for="val in item.options"
:key="val.value"
:label="val.label"
:value="val.value"
></el-option>
</template>
</component>
<slot :name="item.slotName"></slot>
</component>
<slot v-else :name="item.slot" class="tool"></slot>
</template>
</el-form>
<div v-if="isDesc" class="desc">
<slot name="desc"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'SearchArea',
/**
* complete: 有些场景中,需要在弹窗中使用SearchArea,需要调用submitFormData,但弹窗中的插槽挂载晚于弹窗,
* 所以弹窗中的mounted获取不到SearchArea的ref,这里在SearchArea的mounted就绪后触发complete事件,使用者可以监听这个事件来完成一些操作
*/
emits: ['handleQuery', 'complete'],
props: {
formOpt: {
type: Object,
default: () => {
},
},
fieldColumns: {
type: Array,
default: () => [],
},
// 是否有文本内容
isDesc: {
type: Boolean,
default: false,
},
// 传来的表单数据
formDataProp: {
type: Object,
default: () => ({}),
},
inline: {
type: Boolean,
default: true,
},
},
data() {
return {
formData: {},
};
},
mounted() {
if (this.formDataProp) {
console.log(this.formDataProp);
this.formData = this.formDataProp;
}
this.$emit('complete');
},
methods: {
handleChange(v) {
this.$emit('handleQuery', v, this.formData);
},
// 把表单数据给到父组件
submitFormData() {
return this.formData;
},
},
};
</script>
<style lang="scss" scoped>
.searchArea {
padding: 16px 24px 0;
.tool {
width: fit-content;
margin-left: auto;
}
::v-deep .el-form {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
.el-form-item {
display: flex;
align-items: center;
margin: 0 16px 16px 0;
.el-form-item__label {
line-height: 32px;
padding-right: 8px;
font-size: 14px;
font-weight: 400;
color: #333333;
}
.el-form-item__content {
display: flex;
align-items: center;
line-height: 32px;
}
.el-input {
width: 100%;
.el-input__inner {
width: 100%;
height: 32px;
line-height: 32px;
border-radius: 2px;
padding: 0 16px 0 12px;
}
}
.el-input__suffix {
display: grid;
place-content: center;
}
}
}
::v-deep .el-button {
height: 32px;
padding: 0 20px;
line-height: 32px;
border-radius: 2px;
}
.desc {
margin-bottom: 16px;
}
}
</style>
使用方式:
<template>
<search-area
ref="searchFormRef"
:fieldColumns="fieldColumns"
:formDataProp="formData"
:resetBtn="false"
@handleQuery="handleQuery"
></search-area>
</template>
<script>
export default {
data(){
return {
fieldColumns:[
{
name: 'year',
label: '入学年份',
type: 'select',
options: [],//通过接口获取下拉列表
semesterId: '',
},
{
name: 'collegeId',
label: '所属院系',
type: 'select',
options: [],
},
{
name: 'majorId',
label: '专业方向',
type: 'select',
options: [],
},
],
formData: {
year: '',//筛选条件中的默认值
collegeId: '',
majorId: '',
},
}
}
methods:{
//筛选条件改变
handleQuery(v, formData) {
}
}
}
</script>
组件2:有更多查询按钮,折叠显示所有搜索条件
使用方式 :与上述基本一致,在增加一个disabled属性,表示是否需要将搜索条件进行隐藏,换行中显示
如下:
{ name: 'majorId', label: '方向(专业)', type: 'select', width: '360px', options: [], disabled: false, },
<template>
<div class="w-full fold-query-box" :class="{ 'mb-16': !fold }">
<div :class="fold ? '' : 'fold-search--expanded'" class="fold-search inline-flex">
<el-form
v-if="fieldColumns.length > 0"
ref="formRef"
:model="formData"
class="fold-search__area inline-flex"
v-bind="formOpt"
>
<div
ref="areaRef"
:style="{ width: formAreaWidth }"
class="area__form flex flex-wrap items-start justify-start"
>
<template v-for="(item, index) in fieldColumns">
<component
v-if="!item.slot"
:is="'el-form-item'"
:class="['form__item', fold && item.hide ? 'form__item--hide' : '']"
:label="item.label"
:prop="item.name"
:key="index"
v-bind="item.formItemOpt"
>
<component
:is="`el-${item.type}`"
v-model="formData[item.name]"
:disabled="item.disabled || false"
:style="{ width: item.width || '240px' }"
v-bind="item.opt"
@change="onSubmit"
>
<template v-if="item.type === 'select'">
<el-option
v-for="val in item.options"
:key="val.value"
:label="val.label"
:value="val.value"
></el-option>
</template>
</component>
<slot :name="item.slotName"></slot>
</component>
<div v-else :class="['form__item', fold && item.hide ? 'form__item--hide' : '', item.class]" :key="index">
<slot :name="item.slot" />
</div>
</template>
<div v-if="showReset" :class="['form__item', fold ? 'form__item--hide' : '']">
<el-button class="query-btn" @click="handleReset">{{ resetText }}</el-button>
</div>
</div>
<div class="form__toggle cursor-pointer select-none">
<div class="more-search flex items-center justify-center" @click="toggleHide">
<el-divider direction="vertical" />
<span>{{ moreText }}</span>
<i
:class="[fold ? 'more-search__icon--down' : 'more-search__icon--up']"
class="more-search__icon el-icon-d-arrow-left"
></i>
</div>
</div>
</el-form>
</div>
<div class="export-box h-inherit flex flex-1 justify-end pr-24" :class="fold ? 'items-start' : 'items-end'">
<div class="pt-16 flex" :class="[fold ? 'export-btn--expand' : '']">
<slot name="tool" ></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: "FoldSearchArea",
/**
* complete: 有些场景中,需要在弹窗中使用SearchArea,需要调用submitFormData,但弹窗中的插槽挂载晚于弹窗,
* 所以弹窗中的mounted获取不到SearchArea的ref,这里在SearchArea的mounted就绪后触发complete事件,使用者可以监听这个事件来完成一些操作
*/
emits: ["handleQuery", "complete"],
model: {
prop: "value",
event: "update:value",
},
props: {
// 是否折叠。true折叠,false展开
value: {
type: Boolean,
default: true,
},
formOpt: {
type: Object,
default: () => {},
},
fieldColumns: {
type: Array,
default: () => [],
},
// 是否有文本内容
isDesc: {
type: Boolean,
default: false,
},
// 传来的表单数据
formDataProp: {
type: Object,
default: () => {},
},
// 是否显示重置按钮
showReset: {
type: Boolean,
default: false,
},
// 重置按钮的文本
resetText: {
type: String,
default: "重置",
},
moreText: {
type: String,
default: "更多查询",
},
},
data() {
return {
fold: this.value,
formData: {},
formAreaWidth: null,
};
},
watch: {
fold() {
this.$emit("update:value", this.fold);
},
},
computed: {
plainFormItemCount() {
let count = 0;
for (let formItem of this.fieldColumns) {
if (!formItem.hide) {
count++;
} else {
break;
}
}
return count;
},
},
created() {
if (this.formDataProp) {
this.formData = this.formDataProp;
}
// 遍历给formData加属性
for (const item of this.fieldColumns) {
// 用响应式set加,否则输入不上值
this.$set(this.formData, item.name, "");
}
},
mounted() {
if (this.plainFormItemCount > 0) {
let totalWidth = 0;
let children = this.$refs.areaRef.getElementsByClassName("form__item");
Array.prototype.forEach.call(children, (el, count) => {
if (!el.classList.contains("form__item--hide") && count < this.plainFormItemCount) {
totalWidth += el.getBoundingClientRect().width + 16;
}
});
// 遍历第一行显示的元素,计算宽度
this.formAreaWidth = `${totalWidth + 1}px`;
}
this.$emit("complete");
},
methods: {
onSubmit(v) {
this.$emit("handleQuery", this.formData, v);
},
// 把表单数据给到父组件
submitFormData() {
return this.formData;
},
// 切换隐藏状态
toggleHide() {
this.fold = !this.fold;
},
// 重置
handleReset() {
this.$refs.formRef.resetFields();
this.$emit("reset", {});
},
},
};
</script>
<style scoped lang="scss">
.fold-query-box {
display: flex;
.export-box {
.query-btn {
&.export-btn--expand {
margin-bottom: 0;
}
}
}
&--expand {
margin-bottom: 16px;
}
}
::v-deep .el-form {
.el-form-item {
display: flex;
align-items: center;
margin: 0 16px 5px 0;
.el-form-item__label {
line-height: 32px;
padding-right: 8px;
font-size: 14px;
font-weight: 400;
color: #333333;
}
.el-form-item__content {
line-height: 32px;
.el-cascader{
.el-input{
input{
height: 32px !important;
}
}
.el-cascader__tags{
flex-wrap: nowrap;
overflow: hidden;
.el-tag{
max-width: 70% !important;
}
}
}
}
}
.el-input {
width: 100%;
.el-input__inner,input {
width: 100%;
height: 32px;
line-height: 32px;
border-radius: 2px;
padding: 0 16px 0 12px;
}
}
.el-input__suffix {
display: grid;
place-content: center;
}
}
.fold-search {
padding: 16px 24px 0;
transition-property: height, border;
transition-duration: 0.2s;
transition-timing-function: ease-in-out;
&.fold-search--expanded {
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.15);
}
}
.form__item {
margin-bottom: 20px !important;
&.form__item--hide {
display: none;
}
}
.form__toggle {
margin-left: -12px;
}
.more-search {
height: 32px;
color: #3070f9;
span {
margin-left: 4px;
}
.more-search__icon {
margin-left: 4px;
transition: all 0.2s ease-in-out;
&.more-search__icon--up {
transform: rotate(-90deg);
}
&.more-search__icon--down {
transform: rotate(90deg);
}
}
}
</style>