1 mixin 使用场景
针对一般的后台管理系统,会在大量的页面中出现搜索查询功能,此时如果需要封装这些表单和查询条件,则需要采用mixin原理来实现了。
2 mixin 如何实现
由于vue3 setup中没有像vue2中的mixins: []混入,因此需要手动实现它 见代码清单form-table.d.ts文件。
3 mixin 如何使用
在页面中改如何使用它呢,在页面中先引入解构后,在组件中即可使用混入的封装的方法和对象
<script setup lang="ts" name="systemuser">
....
//引入
import FormTableMixin from '@/mixins/form-table.d'
//使用
const service = async (params:object) => {
const { data } = await postListDataRequest('users',params);
return {
data: data.list,
total: data.totalCount,
}
}
const {form, table, params} = FormTableMixin(service,{});
....
</script>
<template>
<div class="systemuser">
<!-- 搜索区 -->
<PageSearch
:config="searchConfig"
:form="form"
></PageSearch>
</div>
</template>
4 form-table.d.ts代码清单
/********* form-table.d.ts文件 ********/
import { ref, reactive, onMounted, computed } from 'vue';
export default function FormTableMixin(service, defaultProps) {
const form = reactive({
value: null||{},
onInput: function(a) {},
onSubmit: function() {}
});
const table = reactive({
data: [],
pagination: {
offset: 1,
size: 15,
total: 0,
...defaultProps,
},
});
// 计算属性
const params = computed(() => {
const { pagination } = table;
return {
...form.value,
offset: pagination.offset,
size: pagination.size,
};
});
// 表单提交事件
form.onSubmit = () => {
table.pagination.offset = 1;
getList();
};
// 表单输入事件
form.onInput = (val) => {
form.value = val;
};
// 分页变化事件
const onChange = (pagination) => {
Object.assign(table.pagination, pagination);
getList()
};
const getList = async () => {
const { data = [], total = 0 } = (await service.call(this, params.value, this)) || {};
table.data = data;
if (table.pagination) {
table.pagination.total = total
}
}
// 返回响应式数据和方法
return {
form,
table,
getList,
params,
onChange,
};
}
5 PageSearch.ts代码清单
<script setup lang='ts'>
import { ref } from 'vue'
import BaseFormItem from './form-item.vue'
import { ElForm } from 'element-plus';
const props = defineProps({
form: {
type: Object,
onInput: {
type: Function
},
onSubmit: {
type: Function
},
value: {
type: Object
}
},
config: {
type: Array,
required: true
}
});
const emit = defineEmits(['submit']);
const searchForm = ref<InstanceType<typeof ElForm> | null>(null);
function initValues() {
if (props.form) {
props.form.value = props.config.reduce((acc: any, item: any) => {
acc[item.prop] = item.value
return acc
}, {})
}
}
const submit = () => {
const submitValues = { ...props.form?.value };
if (props.form?.onSubmit) {
props.form.onSubmit(submitValues)
}
emit('submit', submitValues)
}
const reset = () => {
searchForm.value?.resetFields()
submit()
}
initValues();
defineExpose({
searchForm,
submit,
reset
})
</script>
<template>
<div class='common-search'>
<el-form ref="searchForm" :model="form.value" inline @submit.native.prevent>
<BaseFormItem
v-for="item in config"
class="base-search__form-item"
v-model="form.value[item.prop]"
:key="item.prop"
v-bind="item"
/>
<slot name='footer'>
<el-form-item class='common-search__footer'>
<span @click='reset'>重置</span>
<span @click='submit'>搜索</span>
</el-form-item>
</slot>
</el-form>
</div>
</template>
<style scoped lang="less">
.common-search__footer {
span {
display: inline-block;
text-align: center;
border-radius: 1px;
width: 136px;
height: 36px;
line-height: 36px;
cursor: pointer;
&:first-child {
border: 1px solid blue;
color: blue;
margin-right: 10px;
}
&:last-child {
background: blue;
color: white;
border: 1px solid transparent;
}
}
}
::v-deep {
.base-search__form-item {
margin-right: 40px;
}
.el-input {
width: 260px;
}
.el-select {
width: 260px;
}
}
</style>
6 BaseFormItem.ts代码清单
<template>
<el-form-item size="large" :label="label ? `${label}` : ''" :prop="prop">
<component :is="El[`el-${component}`]" v-model="currentValue" v-bind="options">
<template v-if="component === 'select'">
<el-option
v-for="(item, index) in selectOptions"
:key="index"
:label="item.label"
:value="item.value"
></el-option>
</template>
</component>
</el-form-item>
</template>
<script setup lang="ts">
import { defineProps, defineEmits, computed, useAttrs } from 'vue';
import {ElInput,ElSelect,ElDatePicker} from 'element-plus';
const El = {
'el-input': ElInput,
'el-select': ElSelect,
'el-date-picker': ElDatePicker
}
// 定义属性
const props = defineProps({
modelValue: null,
label: String,
prop: {
type: String,
required: true,
},
component: {
type: String,
required: true,
},
});
// 定义事件
const emit = defineEmits(['update:modelValue']);
// 定义默认选项映射
const defaultOptionMap:any = {
input: {
placeholder: (label:string) => `请输入${label}`,
},
select: {
placeholder: (label:string) => `请选择${label}`,
},
'date-picker': {
placeholder: (label:string) => `请选择${label}`,
rangeSeparator: '至'
},
};
// 计算属性
const currentValue = computed({
get() {
return props.modelValue;
},
set(val) {
emit('update:modelValue', val);
},
});
const options = computed(() => {
const attrs = { ...useAttrs() };
const defaultOption = getDefaultOption();
return {
...defaultOption,
clearable: true,
...attrs,
};
});
const selectOptions = computed(() => {
const attrsOptions = useAttrs().options;
return typeof attrsOptions === 'function' ? attrsOptions() : attrsOptions;
});
// 方法
function getDefaultOption() {
const type = useAttrs().type;
const key = type ? `${props.component}-${type}` : props.component;
const defaultOption = defaultOptionMap[key] || defaultOptionMap[props.component] || {};
let placeholder = defaultOption.placeholder;
if (placeholder) {
placeholder = '';
}
return { ...defaultOption, placeholder };
}
</script>