三、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 方法需要自己定义,根据自己的具体的需求进行修改。

相关推荐
I_Am_Me_4 分钟前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
雯0609~11 分钟前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存
℘团子এ14 分钟前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z20 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
星星会笑滴23 分钟前
vue+node+Express+xlsx+emements-plus实现导入excel,并且将数据保存到数据库
vue.js·excel·express
前端百草阁43 分钟前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜44 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund40444 分钟前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish1 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple1 小时前
typescript里面正则的使用
开发语言·javascript·正则表达式