1. 背景
在我们使用vue3 element-plus-admin后台管理框架当我们表单搜索时,目前该框架找不着对应数值查询范围组件。这时需要我们自己去实现相应的逻辑,看了下对应的源码,可以通过增加动态slot方式来实现。
大概思路如下
1. Search组件增加dynamicSlots数组变量,用于动态渲染增加template, slot模板
2. 子组件插槽重写时,增加ref起始值,结束值变量动态绑定到InputNumber组件上
3. 由于表单方法对slot新增的表单项值获取不到,需要替换@search="setSearchParams" 事件方法修改为自定义的方法,自定义方法将slot定义的ref变量关联到搜索参数上
2. 解决方案
2.1 修改Search.vue
替你Search.vue文件组件内容,代码里主要增加了dynamicSlots属性数组,用于渲染动态slot用
yudao-ui-admin-vue3/src/components/Search/src/Search.vue
<script setup lang="ts">
import { Form } from '@/components/Form'
import { PropType, computed, unref, ref } from 'vue'
import { propTypes } from '@/utils/propTypes'
import { ElButton } from 'element-plus'
import { useI18n } from '@/hooks/web/useI18n'
import { useForm } from '@/hooks/web/useForm'
import { findIndex } from '@/utils'
import { cloneDeep } from 'lodash-es'
import { FormSchema } from '@/types/form'
const { t } = useI18n()
const props = defineProps({
// 生成Form的布局结构数组
schema: {
type: Array as PropType<FormSchema[]>,
default: () => []
},
// 是否需要栅格布局
isCol: propTypes.bool.def(false),
// 表单label宽度
labelWidth: propTypes.oneOfType([String, Number]).def('auto'),
// 操作按钮风格位置
layout: propTypes.string.validate((v: string) => ['inline', 'bottom'].includes(v)).def('inline'),
// 底部按钮的对齐方式
buttomPosition: propTypes.string
.validate((v: string) => ['left', 'center', 'right'].includes(v))
.def('center'),
showSearch: propTypes.bool.def(true),
showReset: propTypes.bool.def(true),
// 是否显示伸缩
expand: propTypes.bool.def(false),
// 伸缩的界限字段
expandField: propTypes.string.def(''),
inline: propTypes.bool.def(true),
dynamicSlots: propTypes.array.def([])
})
const emit = defineEmits(['search', 'reset'])
const visible = ref(true)
const newSchema = computed(() => {
let schema: FormSchema[] = cloneDeep(props.schema)
if (props.expand && props.expandField && !unref(visible)) {
const index = findIndex(schema, (v: FormSchema) => v.field === props.expandField)
if (index > -1) {
const length = schema.length
schema.splice(index + 1, length)
}
}
if (props.layout === 'inline') {
schema = schema.concat([
{
field: 'action',
formItemProps: {
labelWidth: '0px'
}
}
])
}
return schema
})
const { register, elFormRef, methods } = useForm()
const search = async () => {
await unref(elFormRef)?.validate(async (isValid) => {
if (isValid) {
const { getFormData } = methods
const model = await getFormData()
emit('search', model)
}
})
}
const reset = async () => {
unref(elFormRef)?.resetFields()
const { getFormData } = methods
const model = await getFormData()
emit('reset', model)
}
const bottonButtonStyle = computed(() => {
return {
textAlign: props.buttomPosition as unknown as 'left' | 'center' | 'right'
}
})
const setVisible = () => {
unref(elFormRef)?.resetFields()
visible.value = !unref(visible)
}
</script>
<template>
<Form
:is-custom="false"
:label-width="labelWidth"
hide-required-asterisk
:inline="inline"
:is-col="isCol"
:schema="newSchema"
@register="register"
>
<!-- <template #expendAmount>
<slot name="expendAmount"></slot>
</template> -->
<template v-for="item in dynamicSlots" #[item] :key="item">
<slot :name="item"></slot>
</template>
<template #action>
<div v-if="layout === 'inline'">
<ElButton v-if="showSearch" type="primary" @click="search">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</ElButton>
<ElButton v-if="showReset" @click="reset">
<Icon icon="ep:refresh-right" class="mr-5px" />
{{ t('common.reset') }}
</ElButton>
<ElButton v-if="expand" text @click="setVisible">
{{ t(visible ? 'common.shrink' : 'common.expand') }}
<Icon :icon="visible ? 'ep:arrow-up' : 'ep:arrow-down'" />
</ElButton>
</div>
</template>
</Form>
<template v-if="layout === 'bottom'">
<div :style="bottonButtonStyle">
<ElButton v-if="showSearch" type="primary" @click="search">
<Icon icon="ep:search" class="mr-5px" />
{{ t('common.query') }}
</ElButton>
<ElButton v-if="showReset" @click="reset">
<Icon icon="ep:refresh-right" class="mr-5px" />
{{ t('common.reset') }}
</ElButton>
<ElButton v-if="expand" text @click="setVisible">
{{ t(visible ? 'common.shrink' : 'common.expand') }}
<Icon :icon="visible ? 'ep:arrow-up' : 'ep:arrow-down'" />
</ElButton>
</div>
</template>
</template>
2.2 使用示例
<!--金额起始值-->
const expendAmountStart = ref()
<!--金额结束值-->
const expendAmountEnd = ref()
<!--Search表单查询触发函数,slot表单值进行自定义-->
const setSearchParams0 = (data: Recordable) => {
// 动态slot值手动处理并进行设置
data['expendAmount'] = [expendAmountStart.value, expendAmountEnd.value]
setSearchParams(data)
}
<!--Search表单查询slot代码示例-->
<template>
<ContentWrap>
<Search
:schema="allSchemas.searchSchema"
@search="setSearchParams0"
@reset="setSearchParams"
:dynamicSlots="['expendAmount']"
>
<!--支出金额范围查询-->
<template #expendAmount>
<el-input-number
v-model="expendAmountStart"
name="expendAmount"
min="0"
max="100000"
controls-position="right"
/>
<span style="padding: 0 5px">至</span>
<el-input-number
v-model="expendAmountEnd"
name="expendAmount"
:min="0"
:max="100000"
controls-position="right"
/>
</template>
</Search>
</ContentWrap>