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

相关推荐
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
CodeClimb5 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
沈梦研5 小时前
【Vscode】Vscode不能执行vue脚本的原因及解决方法
ide·vue.js·vscode
hunter2062065 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb5 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角5 小时前
CSS 颜色
前端·css
轻口味6 小时前
Vue.js 组件之间的通信模式
vue.js
浪浪山小白兔6 小时前
HTML5 新表单属性详解
前端·html·html5
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter