vue3动态slot数值范围组件实现逻辑思路

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>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值