三、ElementPlus下拉搜索加弹窗组件的封装

近期产品提出了一个需求,要求一个form的表单里面的一个组件既可以下拉模糊搜索,又可以弹窗搜索,我就为这个封装了一个组件,下面看效果图。

效果大家看到了,下面就看组件封装和实现方法

第一步,组件封装,我取名为C_SerachBtn 组件,其中的C_Select组件也可以用el-select组件来代替,C_Select使我们自己封装的组件。

<template>
  <div class="search-box">
    <C_Select
      v-bind="$attrs"
      v-model="_modelValue"
      filterable
      remote
      clearable
      reserve-keyword
      remote-show-suffix
      :remote-method="overhaulProjectCodeMethod"
      :options="_options || []"
      :loading="_loading"
      @focus="focus"
      @change="handleChangeSearchBtn($event)"
    />
    <el-button
      :icon="Search"
      color="#f5f7fa"
      class="search-box-btn"
      @click="handleBtnClick"
    />
  </div>
</template>

<script lang="ts" setup>
import { isFunction } from '@/utils/d_is'
import { Search } from '@element-plus/icons-vue'

interface Props {
  value: any
  label?: any
  option?: any
  options?: any[]
  // query代表的值
  queryValue: string
  // 列表label代表的字段
  labelField?: string
  // 列表label代表的字段
  valueField?: string
  disabledField?: string
  // 下拉数据请求接口
  api?: (arg?: any) => Promise<any>
  // 接口参数
  params?: any
  //返回的值和赋值的值
  callBackNames: any[],
  // 返回列表数据字段
  resultField?: string
  // 是否立即请求接口,否则将在第一次获取焦点时触发请求
  immediate?: boolean
  // 是否多选
  multiple?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  labelField: 'label',
  valueField: 'value',
  disabledField: 'disabled',
  resultField: 'records',
  queryValue:'',
  callBackNames:[],
  immediate: true,
})
const emits = defineEmits([
  'update:value',
  'update:label',
  'update:option',
  'change',
  'visible-change',
  'remove-tag',
  'clear',
  'blur',
  'focus',
  // 下拉接口重新请求,数据更新后触发
  'options-change',
  //按钮点击
  'btn-click',
])
const _selectRef = ref()
const _modelValue = ref(props.value || '')
const _options = ref(props.options || [])
const _option = ref(props.option || {})
const _loading = ref(false)

watch(
  () => props.options,
  (newVal) => {
    if (props.api) return
    _options.value = newVal
  },
  {
    deep: true,
  }
)

watch(
  () => props.option,
  (newVal) => {
    _option.value = newVal
  },
  {
    deep: true,
  }
)

watch(
  () => props.value,
  (newVal) => {
    if (props.multiple && !Array.isArray(newVal)) {
      console.error('multiple 为true时,传入的value数据类型必须为array')
    }
    _modelValue.value = newVal
  },
  {
    immediate: true,
  }
)

watch(
  () => _modelValue.value,
  () => {
    emits('update:value', _modelValue.value)
  },
  {
    immediate: true,
  }
)


//标准项目编号-搜索开始
const overhaulProjectCodeMethod = async (query: string) => {
  if (query) {
    const api = props.api
    if (!api || !isFunction(api)) return
    _options.value = []
    _loading.value = true
    let obj= {
      pageNum: 1,
      pageSize: 10,
      ...props.params,
    }
    obj[props.queryValue] = query
    let res = await api(obj)
    _loading.value = false
    let arr = props.labelField.split(',')
    _options.value = res.records.map((item) => {
      let str =''
      arr.forEach(p=> str += item[p] +' ')
      return {
        label: str,
        value: item[props.valueField],
        name: item[props.valueField],
        key: item[props.valueField],
        ...item,
      }
    })
  } else {
    _options.value = []
  }
}

async function handleChangeSearchBtn(val) {
  if(!val){
    props.callBackNames.forEach(p=>{
      _option.value[p.value]  = ''
    })
    return
  }
  let obj = _options.value.filter(
    (el) => el.value == val
  )[0]
  props.callBackNames.forEach(p=>{
      _option.value[p.value]  = obj[p.name]
  })
  change(val)
}

//按钮点击
const handleBtnClick = () => {
  emits('btn-click', unref(_options))
}

// 下拉接口重新请求,数据更新后触发
const emitChange = () => {
  emits('options-change', unref(_options))
}
// 当 input 获得焦点时触发
const focus = (e) => {
  emits('focus', e)
}
// 选中值发生变化时触发
const change = (val) => {
  let data = _options.value?.filter((x) => x.value == val)
  emits('change', val, data)
}
// 下拉框出现/隐藏时触发
const visibleChange = (val: boolean) => {
  handleFetch()
  emits('visible-change', val)
}
// 多选模式下移除tag时触发
const removeTag = (val) => {
  emits('remove-tag', val)
}
// 可清空的单选模式下用户点击清空按钮时触发
const clear = (e) => {
  emits('clear', e)
}
// 当 input 失去焦点时触发
const blur = (e) => {
  emits('blur', e)
}
const getOptions = () => _options.value
defineExpose({ selectMethods: _selectRef, getOptions })
</script>

<style scoped>
.search-box{
  display: flex;
  width: 100%;
  .search-box-btn{
    border-top-left-radius: 0px;
    border-bottom-left-radius: 0px;
    border-top: 1px solid #dcdfe6;
    border-right: 1px solid #dcdfe6;
    border-bottom: 1px solid #dcdfe6;
    color: #a8abb2;
  }
}
</style>

第二步,页面使用,在页面中el-table中当做slot使用,我的slot取名为 overhaulProjectCode

html 复制代码
  <!-- 标准项目编号 -->
        <template #overhaulProjectCode="{ row, index }">
          <C_SearchBtn
            v-model:value="row.overhaulProjectCode"
            :placeholder="'请选择'"
            :api="ListOverhaulProject"
            :option="row"
            :queryValue="'overhaulCode'"
            :params="{
              deviceCode: 0,
              status: 3,
            }"
            :labelField="'overhaulCode,overhaulName'"
            :valueField="'overhaulCode'"
            :options="[]"
            :callBackNames="[
              {
                name: 'id',
                value: 'overhaulProjectId',
              },
              {
                name: 'overhaulCode',
                value: 'overhaulProjectCode',
              },
              {
                name: 'overhaulName',
                value: 'overhaulProjectName',
              },
            ]"
            @btn-click="handleOverhaulCodeModalVisible(row, index)"
            @focus="handleFocus(index)"
          />
        </template>

第三步,弹窗,和一般的弹窗一样,自行封装。

html 复制代码
<!-- 生产设备 -->
    <materialOne
      title="选择生产设备"
      v-if="materialOneModalVisible"
      :data="curRow"
      v-model:visible="materialOneModalVisible"
      @select="handleMaterialOneSelect2"
      @close="materialOneModalVisible = false"
    />

以上就是基本的做的c_SerachBtn的组件的封装,其中的一些例如handleOverhaulCodeModalVisibl 和 handleFocus 方法需要自己定义,根据自己的具体的需求进行修改。

相关推荐
还是大剑师兰特7 分钟前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
m0_7482361116 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo61728 分钟前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_7482489430 分钟前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_7482356141 分钟前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O3 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink6 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者7 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-8 小时前
验证码机制
前端·后端
燃先生._.9 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js