效果展示
代码
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-col
v-for="item in shows.columns"
:key="item.inputType + JSON.stringify(item.values)"
:span="item.span || 6"
>
<el-form-item v-bind="item.formItemAttrs || {}">
<FormItem
v-if="typeof item.values == 'string'"
v-bind="item.inputAttrs || {}"
v-model="form[item.values]"
:input-type="item.inputType"
/>
<FormItem
v-else
v-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-tooltip
v-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 () => (
<ElDatePicker
modelValue={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>