近期产品提出了一个需求,要求一个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 方法需要自己定义,根据自己的具体的需求进行修改。