效果展示
代码
XXXForm
<template><div class="search-container"><el-form ref="formRef" class="form_is_hidden" :model="form" v-bind="formAttrs"><el-row :gutter="20" class="search-row"><el-colv-for="item in shows.columns":key="item.inputType + JSON.stringify(item.values)":span="item.span || 6"><el-form-item v-bind="item.formItemAttrs || {}"><FormItemv-if="typeof item.values == 'string'"v-bind="item.inputAttrs || {}"v-model="form[item.values]":input-type="item.inputType"/><FormItemv-elsev-model:one="form[item.values[0]]"v-model:two="form[item.values[1]]":input-type="item.inputType"v-bind="item.inputAttrs || {}"/></el-form-item></el-col><el-col :span="shows.btnsSpan" class="search-btn-container"><el-form-item class="search-btn" label="1" label-width="0"><el-tooltipv-if="shows.collapsedBtn":content="collapsed ? '收起' : '展开'"placement="top"trigger="hover"><el-button class="coll" text bg type="info" @click="toggleCollapse"><el-icon class="form_is_hidden_icon"><DArrowRight v-if="!collapsed" /><DArrowLeft v-else /></el-icon></el-button></el-tooltip><el-button text bg type="info" @click="resetForm">重置</el-button><el-button type="primary" @click="search">查询</el-button></el-form-item></el-col></el-row></el-form></div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { useVModel } from '@vueuse/core';
import { SearchFormColum } from './type';
import FormItem from './formItem.vue';
import { DArrowRight, DArrowLeft } from '@element-plus/icons-vue';const props = defineProps({columns: {type: Array,default: () => [],},collapse: {type: Boolean,default: false,},modelValue: {type: Object,default: () => {return {};},},resttForm: {type: Object,default: () => {return {};},},formAttrs: {type: Object,default: () => {return {};},},
});
const emit = defineEmits(['update:modelValue', 'search', 'reset']);
const collapsed = ref(props.collapse);
const form = useVModel(props, 'modelValue', emit);
const shows = computed(() => {const cols = props.columns as SearchFormColum;const spans = cols?.map((i) => i.span || 6).reduce((a, b) => a + b, 0);if (spans > 18) {if (!collapsed.value) {let spanss = 0;let index = 0;try {cols?.forEach((i, idx) => {if (spanss < 18) {if ((i.span || 6) + spanss > 18) {throw new Error('');}spanss += i.span || 6;index = idx;} else {throw new Error('');}});} catch (error) {// console.log('跳出循环');}return {columns: cols.filter((_i, idx) => idx <= index),btnsSpan: 24 - spanss,collapsedBtn: true,};} else {const spanss = cols?.map((i) => i.span || 6).reduce((a, b) => {if (a + b > 24) {return b;} else if (a + b == 24) {return 0;} else {return a + b;}}, 0);const btnsSpan = spanss > 18 ? 24 : 24 - spanss;return {columns: cols,btnsSpan,collapsedBtn: true,};}} else {return {columns: cols,btnsSpan: 24 - spans,collapsedBtn: false,};}
});
const search = () => {emit('search');
};
const resetForm = () => {const resett = { ...props.resttForm };form.value = resett;emit('reset');
};
const toggleCollapse = () => {collapsed.value = !collapsed.value;
};
</script>
<style lang="less" scoped>
.search-container {// margin-bottom: 20px;.form_is_hidden_icon {transform: rotate(90deg);}.search-btn-container {.search-btn {:deep(.el-form-item__content) {justify-content: end;}:deep(.el-form-item__label) {color:transparent;}}}.search-row {// transition: height 0.3s;overflow: hidden;}.search-label {font-size: 14px;color: var(--hiwork-global-color);padding-bottom: 8px;}
}
:deep(.el-button).coll {padding: 10px 12px !important;
}
</style>
formItem
<template><component :is="currentCom" />
</template>
<script lang="tsx" setup>
// import cityCascader from './cityCascader';
import DateMonthPicker from './components/dateMonthPicker';
import DateRangePicker from './components/dateRangePicker';
import DateYearPicker from './components/dateYearPicker';
import Select from './components/select';
import CountryAutoComplete from './components/countryAutoComplete.vue';
import CountryAutoCompleteOne from './components/countryAutoCompleteOne.vue';
import HiInput from './components/hiInput.vue';
import { computed } from 'vue';
const props = defineProps({inputType: {type: String,default: 'input',},
});const currentCom = computed(() => {switch (props.inputType) {case 'date':return DateRangePicker;case 'month':return DateMonthPicker;case 'year':return DateYearPicker;// case 'cascader':// return cityCascader;case 'select':return Select;case 'countryAuto':return CountryAutoComplete;case 'countryAuto1':return CountryAutoCompleteOne;default:return HiInput;}
});
</script>
XXXInput
<template><!-- hiInput --><el-input v-model="modelValue" @blur="hBlur" />
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({modelValue: {type: String,default: '',},
});
const emit = defineEmits(['update:modelValue']);
const modelValue = computed({get() {return props.modelValue;},set(v) {emit('update:modelValue', v);},
});
const hBlur = () => {modelValue.value = modelValue.value.trim();
};
</script>
// dateRangePicker
import { ElDatePicker } from 'element-plus';
import { defineComponent, reactive, watchEffect } from 'vue';
import dayjs from 'dayjs';export default defineComponent({props: {one: { type: String, default: () => '' },two: { type: String, default: () => '' },format: { type: String, default: () => '' },},emits: ['update:one', 'update:two'],// emits: {// 'update:one': (v: string) => true,// 'update:two': (v: string) => true,// },setup(props, { attrs, emit }) {const state = reactive({ivalue: [],});const updateValue = (v: (string | number | Date | dayjs.Dayjs | null | undefined)[],) => {emit('update:one',v ? dayjs(v[0]).format(props.format || 'YYYY-MM-DD') : '',);emit('update:two',v ? dayjs(v[1]).format(props.format || 'YYYY-MM-DD') : '',);};watchEffect(() => {state.ivalue = [props.one, props.two] as any;});return () => (<ElDatePickermodelValue={state.ivalue as any}onUpdate:modelValue={updateValue}type='daterange'range-separator='-'start-placeholder='开始'end-placeholder='结束'{...attrs}/>);},
});
// cityCascader
import { getLocationDetailInfo } from '@/axios/api';
import { ElCascader } from 'element-plus';
import { defineComponent, ref } from 'vue';export default defineComponent((_p, { attrs }) => {const cityList = ref([]);getLocationDetailInfo().then((res) => {cityList.value = res.data;});return () => (<ElCascader{...attrs}placeholder='请选择省市区'style={'width: 100%'}props={{ children: 'child', value: 'name', label: 'name' }}options={cityList.value}/>);
});
XXXInput,自己想要啥补充啥就行。
使用
props
参数 说明 类型 可选值 默认值 columns 配置表单展示项 object[] - - collapse 是否收起 boolean true/false false modelValue form 对象 {} - {} resttForm 重制 form 默认值 {} - {} formAttrs 支持 el 的 form 属性 {} - {}
cloumns 说明
参数 说明 类型 可选值 默认值 inputType 输入框类型 string - - span formitem 占用空间(参考el-col) number 1-24 6 formItemAttrs 继承 el 的 formitem 的 props + emit,配置formitem 像 el 一样。 {} - - values form 中对应的 key string/string[] - - inputAttrs 继承 el 的 input 的 props + emit,配置 input 像 el 一样。也是拓展属性的地方。 {} - -
emit
方法名 说明 类型 update:modelValue 更新 form 对象 - search 搜索 - reset 重制 -
使用示例
searchFormC
import { defineComponent, ref } from 'vue';
//@ts-ignore
import SearchForm from '@/components/searchForm/index.vue';
export default defineComponent((_p, { attrs }) => {const columns = ref([{values: 'nickname',inputType: 'input',span: 6,inputAttrs: {placeholder: '请输入',},formItemAttrs: {label: '姓名',labelWidth: '80px',},},{values: 'country',inputType: 'countryAuto1',inputAttrs: {placeholder: '请输入',},formItemAttrs: {label: '国家',labelWidth: '80px',},},{values: 'email',inputType: 'input',span: 6,inputAttrs: {placeholder: '请输入',},formItemAttrs: {label: '邮箱',labelWidth: '80px',},},{values: 'employeeCode',inputType: 'input',span: 12,inputAttrs: {placeholder: '请输入',},formItemAttrs: {label: '员工编号',labelWidth: '80px',},},{values: ['createStartTime', 'createEndTime'],inputType: 'date',span: 12,inputAttrs: {format: 'YYYY-MM-DD',placeholder: '请输入',},formItemAttrs: {label: '创建时间',labelWidth: '80px',},},]);return () => (<SearchForm {...attrs} columns={columns.value} collapse={false} formAttrs={{ labelPosition: 'left' }} />);
});
vue
<template><SearchFormC v-model="form" @search="search" @reset="rest" />
</template><script setup lang="ts">
import SearchFormC from './components/searchFormC'
import { ref } from 'vue';
const form = ref({})const search = () => {console.log(form.value)
}const rest = () => {console.log(form.value)
}
</script>