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

打印 上一主题 下一主题

主题 874|帖子 874|积分 2622

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



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

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


  1. <template>
  2.   <div class="search-box">
  3.     <C_Select
  4.       v-bind="$attrs"
  5.       v-model="_modelValue"
  6.       filterable
  7.       remote
  8.       clearable
  9.       reserve-keyword
  10.       remote-show-suffix
  11.       :remote-method="overhaulProjectCodeMethod"
  12.       :options="_options || []"
  13.       :loading="_loading"
  14.       @focus="focus"
  15.       @change="handleChangeSearchBtn($event)"
  16.     />
  17.     <el-button
  18.       :icon="Search"
  19.       color="#f5f7fa"
  20.       class="search-box-btn"
  21.       @click="handleBtnClick"
  22.     />
  23.   </div>
  24. </template>
  25. <script lang="ts" setup>
  26. import { isFunction } from '@/utils/d_is'
  27. import { Search } from '@element-plus/icons-vue'
  28. interface Props {
  29.   value: any
  30.   label?: any
  31.   option?: any
  32.   options?: any[]
  33.   // query代表的值
  34.   queryValue: string
  35.   // 列表label代表的字段
  36.   labelField?: string
  37.   // 列表label代表的字段
  38.   valueField?: string
  39.   disabledField?: string
  40.   // 下拉数据请求接口
  41.   api?: (arg?: any) => Promise<any>
  42.   // 接口参数
  43.   params?: any
  44.   //返回的值和赋值的值
  45.   callBackNames: any[],
  46.   // 返回列表数据字段
  47.   resultField?: string
  48.   // 是否立即请求接口,否则将在第一次获取焦点时触发请求
  49.   immediate?: boolean
  50.   // 是否多选
  51.   multiple?: boolean
  52. }
  53. const props = withDefaults(defineProps<Props>(), {
  54.   labelField: 'label',
  55.   valueField: 'value',
  56.   disabledField: 'disabled',
  57.   resultField: 'records',
  58.   queryValue:'',
  59.   callBackNames:[],
  60.   immediate: true,
  61. })
  62. const emits = defineEmits([
  63.   'update:value',
  64.   'update:label',
  65.   'update:option',
  66.   'change',
  67.   'visible-change',
  68.   'remove-tag',
  69.   'clear',
  70.   'blur',
  71.   'focus',
  72.   // 下拉接口重新请求,数据更新后触发
  73.   'options-change',
  74.   //按钮点击
  75.   'btn-click',
  76. ])
  77. const _selectRef = ref()
  78. const _modelValue = ref(props.value || '')
  79. const _options = ref(props.options || [])
  80. const _option = ref(props.option || {})
  81. const _loading = ref(false)
  82. watch(
  83.   () => props.options,
  84.   (newVal) => {
  85.     if (props.api) return
  86.     _options.value = newVal
  87.   },
  88.   {
  89.     deep: true,
  90.   }
  91. )
  92. watch(
  93.   () => props.option,
  94.   (newVal) => {
  95.     _option.value = newVal
  96.   },
  97.   {
  98.     deep: true,
  99.   }
  100. )
  101. watch(
  102.   () => props.value,
  103.   (newVal) => {
  104.     if (props.multiple && !Array.isArray(newVal)) {
  105.       console.error('multiple 为true时,传入的value数据类型必须为array')
  106.     }
  107.     _modelValue.value = newVal
  108.   },
  109.   {
  110.     immediate: true,
  111.   }
  112. )
  113. watch(
  114.   () => _modelValue.value,
  115.   () => {
  116.     emits('update:value', _modelValue.value)
  117.   },
  118.   {
  119.     immediate: true,
  120.   }
  121. )
  122. //标准项目编号-搜索开始
  123. const overhaulProjectCodeMethod = async (query: string) => {
  124.   if (query) {
  125.     const api = props.api
  126.     if (!api || !isFunction(api)) return
  127.     _options.value = []
  128.     _loading.value = true
  129.     let obj= {
  130.       pageNum: 1,
  131.       pageSize: 10,
  132.       ...props.params,
  133.     }
  134.     obj[props.queryValue] = query
  135.     let res = await api(obj)
  136.     _loading.value = false
  137.     let arr = props.labelField.split(',')
  138.     _options.value = res.records.map((item) => {
  139.       let str =''
  140.       arr.forEach(p=> str += item[p] +' ')
  141.       return {
  142.         label: str,
  143.         value: item[props.valueField],
  144.         name: item[props.valueField],
  145.         key: item[props.valueField],
  146.         ...item,
  147.       }
  148.     })
  149.   } else {
  150.     _options.value = []
  151.   }
  152. }
  153. async function handleChangeSearchBtn(val) {
  154.   if(!val){
  155.     props.callBackNames.forEach(p=>{
  156.       _option.value[p.value]  = ''
  157.     })
  158.     return
  159.   }
  160.   let obj = _options.value.filter(
  161.     (el) => el.value == val
  162.   )[0]
  163.   props.callBackNames.forEach(p=>{
  164.       _option.value[p.value]  = obj[p.name]
  165.   })
  166.   change(val)
  167. }
  168. //按钮点击
  169. const handleBtnClick = () => {
  170.   emits('btn-click', unref(_options))
  171. }
  172. // 下拉接口重新请求,数据更新后触发
  173. const emitChange = () => {
  174.   emits('options-change', unref(_options))
  175. }
  176. // 当 input 获得焦点时触发
  177. const focus = (e) => {
  178.   emits('focus', e)
  179. }
  180. // 选中值发生变化时触发
  181. const change = (val) => {
  182.   let data = _options.value?.filter((x) => x.value == val)
  183.   emits('change', val, data)
  184. }
  185. // 下拉框出现/隐藏时触发
  186. const visibleChange = (val: boolean) => {
  187.   handleFetch()
  188.   emits('visible-change', val)
  189. }
  190. // 多选模式下移除tag时触发
  191. const removeTag = (val) => {
  192.   emits('remove-tag', val)
  193. }
  194. // 可清空的单选模式下用户点击清空按钮时触发
  195. const clear = (e) => {
  196.   emits('clear', e)
  197. }
  198. // 当 input 失去焦点时触发
  199. const blur = (e) => {
  200.   emits('blur', e)
  201. }
  202. const getOptions = () => _options.value
  203. defineExpose({ selectMethods: _selectRef, getOptions })
  204. </script>
  205. <style scoped>
  206. .search-box{
  207.   display: flex;
  208.   width: 100%;
  209.   .search-box-btn{
  210.     border-top-left-radius: 0px;
  211.     border-bottom-left-radius: 0px;
  212.     border-top: 1px solid #dcdfe6;
  213.     border-right: 1px solid #dcdfe6;
  214.     border-bottom: 1px solid #dcdfe6;
  215.     color: #a8abb2;
  216.   }
  217. }
  218. </style>
复制代码
第二步,页面使用,在页面中el-table中当做slot使用,我的slot取名为 overhaulProjectCode
  1.   <!-- 标准项目编号 -->
  2.         <template #overhaulProjectCode="{ row, index }">
  3.           <C_SearchBtn
  4.             v-model:value="row.overhaulProjectCode"
  5.             :placeholder="'请选择'"
  6.             :api="ListOverhaulProject"
  7.             :option="row"
  8.             :queryValue="'overhaulCode'"
  9.             :params="{
  10.               deviceCode: 0,
  11.               status: 3,
  12.             }"
  13.             :labelField="'overhaulCode,overhaulName'"
  14.             :valueField="'overhaulCode'"
  15.             :options="[]"
  16.             :callBackNames="[
  17.               {
  18.                 name: 'id',
  19.                 value: 'overhaulProjectId',
  20.               },
  21.               {
  22.                 name: 'overhaulCode',
  23.                 value: 'overhaulProjectCode',
  24.               },
  25.               {
  26.                 name: 'overhaulName',
  27.                 value: 'overhaulProjectName',
  28.               },
  29.             ]"
  30.             @btn-click="handleOverhaulCodeModalVisible(row, index)"
  31.             @focus="handleFocus(index)"
  32.           />
  33.         </template>
复制代码
第三步,弹窗,和一般的弹窗一样,自行封装。
  1. <!-- 生产设备 -->
  2.     <materialOne
  3.       title="选择生产设备"
  4.       v-if="materialOneModalVisible"
  5.       :data="curRow"
  6.       v-model:visible="materialOneModalVisible"
  7.       @select="handleMaterialOneSelect2"
  8.       @close="materialOneModalVisible = false"
  9.     />
复制代码
以上就是基本的做的c_SerachBtn的组件的封装,其中的一些比方handleOverhaulCodeModalVisibl  和 handleFocus   方法需要自己定义,根据自己的详细的需求举行修改。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农妇山泉一亩田

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表