Element-plus封裝搜索組件的實(shí)現(xiàn)
前言
在后臺(tái)管理系統(tǒng)中,經(jīng)常需要在多個(gè)頁面中使用搜索功能。搜索的形式可以包括文本搜索、時(shí)間范圍搜索、選擇框搜索、日期搜索和多級(jí)聯(lián)選搜索等。然而,在每個(gè)頁面中編寫重復(fù)的搜索功能代碼會(huì)導(dǎo)致效率低下且代碼冗余。為了解決這個(gè)問題,我們可以封裝一個(gè)通用的搜索組件,以節(jié)省開發(fā)時(shí)間和減少重復(fù)代碼的量。(僅供參考,歡迎評(píng)論區(qū)jym提出更好的意見)
首先,讓我們分析頁面布局和功能需求:
- 搜索組件本質(zhì)上是一個(gè)表單。
- 表單中包含了文本框、選擇框、級(jí)聯(lián)選擇器和日期選擇器等不同類型的輸入控件。
- 當(dāng)點(diǎn)擊搜索按鈕時(shí),應(yīng)觸發(fā)搜索事件;當(dāng)點(diǎn)擊重置按鈕時(shí),應(yīng)觸發(fā)重置事件。
基于上述分析,我們可以逐步實(shí)現(xiàn)具體的功能。請(qǐng)按照以下步驟進(jìn)行操作:
- 創(chuàng)建一個(gè)新的 Vue 3 項(xiàng)目:npm create vue@latest
- 刪除項(xiàng)目中自動(dòng)生成的文件和內(nèi)容。
- 在項(xiàng)目中安裝 Element Plus:npm install element-plus --save
在components目錄中創(chuàng)建 mySearch 組件,包含了一個(gè)基于 Element Plus 的表單 (el-form)。表單的屬性包括大小 (size)、數(shù)據(jù)模型 (model) 和標(biāo)簽寬度 (label-width),這些屬性通過組件的 props 傳遞進(jìn)來。
在 el-row 元素中,使用了 v-for 指令來遍歷 props.option 數(shù)組,該數(shù)組用于配置不同類型的搜索輸入控件。
搜索按鈕 (el-button) 和重置按鈕 (el-button),它們分別綁定了 search 方法和 reset 方法。這些方法會(huì)在用戶點(diǎn)擊按鈕時(shí)觸發(fā)相應(yīng)的事件。
<template> <div> <el-form ref="searchRef" :size="props.size" :model="searchVal" :label-width="props.labelWidth"> <el-row :gutter="20"> <el-col :xs="24" :sm="12" :md="12" :lg="props.span" v-for="(item, index) in props.option" :key="index" class="mb20"> </el-col> <el-col :xs="24" :sm="12" :md="12" :lg="props.span"> <el-form-item> <el-button type="primary" @click="search">搜索</el-button> <el-button @click="reset(searchRef)">重置</el-button> </el-form-item> </el-col> </el-row> </el-form> </div> </template> <script lang='ts' setup> import { onMounted, reactive, ref } from 'vue' import type { ElForm } from 'element-plus' import { ElMessage } from 'element-plus' import { formatDate } from '@/utils/formatTime' import type { Option } from '@/components/type.d.ts' type FormInstance = InstanceType<typeof ElForm> const searchRef = ref<FormInstance>() // 搜索值 let searchVal: any = reactive({}) // 搜索 const search = () => { for (let i in searchVal) { if (searchVal[i] === undefined || searchVal[i] === '') { delete searchVal[i] } } emit('search', searchVal) } // 重置 const reset = (formEl: FormInstance | undefined) => { if (!formEl) return formEl.resetFields() emit('search', searchVal) } interface Props { labelWidth?: number | string size?: 'small' | 'default' | 'large' span?: number option: Option[] defaultValue?: any } const props = withDefaults(defineProps<Props>(), { size: 'default', span: 8, labelWidth: 100 }) </script>
聲明了一個(gè)類型別名 FormInstance,用于表示表單的實(shí)例。通過 ref 函數(shù)創(chuàng)建了一個(gè)響應(yīng)式引用 searchRef,用于引用表單實(shí)例。使用 reactive 函數(shù)創(chuàng)建了一個(gè)響應(yīng)式對(duì)象 searchVal,用于存儲(chǔ)搜索的值。
定義 search 方法,該方法會(huì)在搜索按鈕被點(diǎn)擊時(shí)觸發(fā)。遍歷 searchVal 對(duì)象的屬性,如果屬性值為空或未定義,則從 searchVal 中刪除該屬性。最后,我們通過 emit 方法觸發(fā) search 的自定義事件,并將 searchVal 作為參數(shù)傳遞給父組件。定義了 reset 方法,該方法會(huì)在重置按鈕被點(diǎn)擊時(shí)觸發(fā)。傳入的表單實(shí)例調(diào)用 resetFields 方法,將表單重置為初始狀態(tài)。最后,再次通過 emit 方法觸發(fā)search 的自定義事件,并將 searchVal 作為參數(shù)傳遞給父組件。
接下來分析option中的數(shù)據(jù),新建type.d.ts
//選擇框的option類型 interface SelectOption { label: string value: any } //選擇框和文本輸入框的option類型 interface InputOptions { prop: string option: SelectOption[] } export interface Option { label: string //標(biāo)簽名稱 prop: string //表單項(xiàng)字段名 endProp?: string //日期范圍的結(jié)束日期字段名 placeholder?: string //輸入框占位文本 type?: 'input' | 'select' | 'cascader' | 'date' | 'datetime' | 'daterange' | 'inputSelect' //表單項(xiàng)類型 optionLabel?: string //選擇框的label字段 optionValue?: string //選擇框的value字段 option?: any[] //選擇框option inputOptions?: InputOptions //選擇框和文本輸入框的option max?: number //級(jí)聯(lián)選擇器的最大選擇數(shù)量 children?: string //級(jí)聯(lián)選擇器children字段名 multiple?: boolean //級(jí)聯(lián)選擇器是否多選 value?: string //級(jí)聯(lián)選擇器返回值字段名 itemLabel?: string //級(jí)聯(lián)選擇器選項(xiàng)名稱 checkStrictly?: boolean //是否可選擇父節(jié)點(diǎn)和子節(jié)點(diǎn) emitPath?: boolean // 是否返回由該節(jié)點(diǎn)所在的各級(jí)菜單的值所組成的數(shù)組 show?: boolean //是否顯示選中值的完整路徑 filterable?: boolean //選擇框是否可搜索 } 完善表單,根據(jù)實(shí)際需求,暴露出表單的配置項(xiàng),完整代碼如下 ini 代碼解讀復(fù)制代碼<template> <div> <el-form ref="searchRef" :size="props.size" :model="searchVal" :label-width="props.labelWidth"> <el-row :gutter="20"> <el-col :xs="24" :sm="12" :md="12" :lg="props.span" v-for="(item, index) in props.option" :key="index" class="mb20"> <el-form-item v-if="item.type == 'input' || !item.type" @keyup.enter="search" :prop="item.prop" :label="item.label"> <el-input v-model="searchVal[item.prop]" :placeholder="item.placeholder"></el-input> </el-form-item> <el-form-item v-if="item.type == 'inputSelect'" label-width="0" :prop="item.prop"> <el-input v-model="searchVal[item.prop]" clearable :placeholder="item.placeholder"> <template #prepend> <el-select v-model="searchVal[item.inputOptions.prop]" :style="{ width: props.labelWidth + 'px' }"> <el-option v-for="(sItem, sIndex) in item.inputOptions.option" :key="sIndex" :label="sItem.label" :value="sItem.value"></el-option> </el-select> </template> </el-input> </el-form-item> <el-form-item v-else-if="item.type == 'select'" :prop="item.prop" :label="item.label"> <el-select v-model="searchVal[item.prop]" :filterable="item.filterable" :fit-input-width="true" style="width: 100%" :placeholder="item.placeholder"> <el-option v-for="(sItem, sIndex) in item.option" :key="sIndex" :label="sItem[item.optionLabel || 'label']" :value="sItem[item.optionValue || 'value']"></el-option> </el-select> </el-form-item> <el-form-item v-else-if="item.type == 'cascader'" :prop="item.prop" :label="item.label"> <el-cascader v-model="searchVal[item.prop]" :options="item.option" :placeholder="item.placeholder" clearable :props="{ value: item.value || 'value', label: item.itemLabel || 'label', children: item.children || 'children', multiple: item.multiple || false, emitPath: item.emitPath || false, checkStrictly: item.checkStrictly || false }" :show-all-levels="item.show || false" @change="(val: any) => changeType(val, item)" /> </el-form-item> <el-form-item v-else-if="item.type == 'date'" :prop="item.prop" :label="item.label"> <el-date-picker v-model="searchVal[item.prop]" type="date" value-format="YYYY-MM-DD" format="YYYY-MM-DD" :placeholder="item.placeholder" style="width: 100%" ></el-date-picker> </el-form-item> <el-form-item v-else-if="item.type == 'datetime'" :prop="item.prop" :label="item.label"> <el-date-picker v-model="searchVal[item.prop]" type="datetime" value-format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm" :placeholder="item.placeholder" style="width: 100%" ></el-date-picker> </el-form-item> <el-form-item v-else-if="item.type == 'daterange'" :prop="item.prop" :label="item.label"> <el-date-picker v-model="searchVal[item.prop + 'Range']" style="width: 100%" type="daterange" range-separator="~" start-placeholder="開始時(shí)間" end-placeholder="結(jié)束時(shí)間" value-format="YYYY-MM-DD" format="YYYY-MM-DD" @change="changeDateRange(item)" > </el-date-picker> </el-form-item> </el-col> <el-col :xs="24" :sm="12" :md="12" :lg="props.span"> <el-form-item> <el-button type="primary" @click="search">搜索</el-button> <el-button @click="reset(searchRef)">重置</el-button> </el-form-item> </el-col> </el-row> </el-form> </div> </template> <script lang="ts" setup> import { onMounted, reactive, ref } from 'vue' import type { ElForm } from 'element-plus' import { ElMessage } from 'element-plus' import { formatDate } from '@/utils/formatTime' import type { Option } from '@/components/type.d.ts' type FormInstance = InstanceType<typeof ElForm> const searchRef = ref<FormInstance>() // 搜索值 let searchVal: any = reactive({}) // 搜索 const search = () => { for (let i in searchVal) { if (searchVal[i] === undefined || searchVal[i] === '') { delete searchVal[i] } } emit('search', searchVal) } // 重置 const reset = (formEl: FormInstance | undefined) => { if (!formEl) return formEl.resetFields() emit('search', searchVal) } // 級(jí)聯(lián)選擇 const changeType = (value: any, data: Option) => { if (!value) value = [] if (value.length > (data.max as number)) { searchVal[data.prop] = value.slice(0, 4) ElMessage.error('館校類型數(shù)量限制為4個(gè)') } } // 日期范圍選擇 const changeDateRange = (item: any) => { let dateArr = searchVal[item.prop + 'Range'] if (dateArr) { searchVal[item.prop] = formatDate(dateArr[0], 'YYYY-mm-dd') searchVal[item.endProp] = formatDate(dateArr[1], 'YYYY-mm-dd') } else { searchVal[item.prop] = '' searchVal[item.endProp] = '' } } const emit = defineEmits(['search', 'reset']) interface Props { labelWidth?: number | string size?: 'small' | 'default' | 'large' span?: number option: Option[] defaultValue?: any } const props = withDefaults(defineProps<Props>(), { size: 'default', span: 8, labelWidth: 100 }) onMounted(() => { if (props.defaultValue) { props.option.forEach((p: any) => { if (p.type == 'daterange' && props.defaultValue[p.prop]) { searchVal[p.prop + 'Range'] = [] if (props.defaultValue[p.prop]) { searchVal[p.prop + 'Range'][0] = props.defaultValue[p.prop] } if (props.defaultValue[p.endProp]) { searchVal[p.prop + 'Range'][1] = props.defaultValue[p.endProp] } } }) searchVal = Object.assign(searchVal, props.defaultValue) } }) </script> <style lang="scss" scoped></style>
實(shí)際使用,App.vue中
<template> <div class="main"> <MySearch :option="searchOptions" :defaultValue="{ daterange: '', endDaterange: '' }" @search="search" /> </div> </template> <script setup lang="ts"> import { ref, reactive } from 'vue' import MySearch from './components/mySearch.vue' import type { Option } from '@/components/type.d.ts' const cascaderOptions = [ { value: 'guide', label: 'Guide', children: [ { value: 'disciplines', label: 'Disciplines', children: [ { value: 'consistency', label: 'Consistency' }, { value: 'feedback', label: 'Feedback' }, { value: 'efficiency', label: 'Efficiency' }, { value: 'controllability', label: 'Controllability' } ] }, { value: 'navigation', label: 'Navigation', children: [ { value: 'side nav', label: 'Side Navigation' }, { value: 'top nav', label: 'Top Navigation' } ] } ] }, { value: 'component', label: 'Component', children: [ { value: 'basic', label: 'Basic', children: [ { value: 'layout', label: 'Layout' }, { value: 'color', label: 'Color' }, { value: 'typography', label: 'Typography' }, { value: 'icon', label: 'Icon' }, { value: 'button', label: 'Button' } ] }, { value: 'form', label: 'Form', children: [ { value: 'radio', label: 'Radio' }, { value: 'checkbox', label: 'Checkbox' }, { value: 'input', label: 'Input' }, { value: 'input-number', label: 'InputNumber' }, { value: 'select', label: 'Select' }, { value: 'cascader', label: 'Cascader' }, { value: 'switch', label: 'Switch' }, { value: 'slider', label: 'Slider' }, { value: 'time-picker', label: 'TimePicker' }, { value: 'date-picker', label: 'DatePicker' }, { value: 'datetime-picker', label: 'DateTimePicker' }, { value: 'upload', label: 'Upload' }, { value: 'rate', label: 'Rate' }, { value: 'form', label: 'Form' } ] }, { value: 'data', label: 'Data', children: [ { value: 'table', label: 'Table' }, { value: 'tag', label: 'Tag' }, { value: 'progress', label: 'Progress' }, { value: 'tree', label: 'Tree' }, { value: 'pagination', label: 'Pagination' }, { value: 'badge', label: 'Badge' } ] }, { value: 'notice', label: 'Notice', children: [ { value: 'alert', label: 'Alert' }, { value: 'loading', label: 'Loading' }, { value: 'message', label: 'Message' }, { value: 'message-box', label: 'MessageBox' }, { value: 'notification', label: 'Notification' } ] }, { value: 'navigation', label: 'Navigation', children: [ { value: 'menu', label: 'Menu' }, { value: 'tabs', label: 'Tabs' }, { value: 'breadcrumb', label: 'Breadcrumb' }, { value: 'dropdown', label: 'Dropdown' }, { value: 'steps', label: 'Steps' } ] }, { value: 'others', label: 'Others', children: [ { value: 'dialog', label: 'Dialog' }, { value: 'tooltip', label: 'Tooltip' }, { value: 'popover', label: 'Popover' }, { value: 'card', label: 'Card' }, { value: 'carousel', label: 'Carousel' }, { value: 'collapse', label: 'Collapse' } ] } ] }, { value: 'resource', label: 'Resource', children: [ { value: 'axure', label: 'Axure Components' }, { value: 'sketch', label: 'Sketch Templates' }, { value: 'docs', label: 'Design Documentation' } ] } ] const searchOptions: Option[] = reactive([ { label: '文本類型', placeholder: '請(qǐng)輸入文本', type: 'input', prop: 'input' }, { label: '選擇框', prop: 'select', placeholder: '請(qǐng)選擇支付方式', type: 'select', option: [ { label: '微信', value: 0 }, { label: '支付寶', value: 1 } ] }, { label: '文本選擇框', prop: 'inputSelect1', placeholder: '請(qǐng)輸入文本', type: 'inputSelect', inputOptions: { prop: 'inputSelect2', option: [ { label: '微信', value: 0 }, { label: '支付寶', value: 1 } ] } }, { label: '級(jí)聯(lián)選擇器', placeholder: '請(qǐng)選擇級(jí)聯(lián)選擇器', type: 'cascader', prop: 'cascader', option: cascaderOptions }, { label: '日期選擇', type: 'date', prop: 'date', placeholder: '請(qǐng)選擇日期' }, { label: '日期時(shí)間', type: 'datetime', prop: 'datetime', placeholder: '請(qǐng)選擇日期時(shí)間' }, { label: '日期范圍', type: 'daterange', prop: 'daterange', endProp: 'endDaterange' } ]) // 搜索 const search = (val: any) => { tableData.searchForm = val tableData.page.pageIndex = 1 initTableData() } //表格數(shù)據(jù) const tableData: any = reactive({ data: [], searchForm: {}, page: { pageIndex: 1, pageSize: 10, total: 0 } }) // 初始化表格數(shù)據(jù) const initTableData = () => { console.log(tableData.searchForm) } </script> <style scoped> .main { padding: 20px; } </style>
總結(jié):
實(shí)現(xiàn)了一個(gè)搜索組件,根據(jù) props.option 數(shù)組動(dòng)態(tài)生成不同類型的表單項(xiàng),以便用戶輸入搜索條件。
- 代碼中的 el-col 元素表示一個(gè)列布局,用于展示生成的表單項(xiàng)。
- 根據(jù) props.option 數(shù)組的每一項(xiàng) item,根據(jù) item.type 的不同選擇不同的表單項(xiàng)類型進(jìn)行渲染。
- 支持的表單項(xiàng)類型包括輸入框、帶下拉選擇框的輸入框、選擇框、級(jí)聯(lián)選擇器、日期選擇器和日期范圍選擇器。
- 每個(gè)表單項(xiàng)都綁定了相應(yīng)的數(shù)據(jù)模型,通過 v-model 實(shí)現(xiàn)了數(shù)據(jù)的雙向綁定。
- 表單項(xiàng)的標(biāo)簽、屬性和占位符等信息都是根據(jù) item 對(duì)象中的屬性動(dòng)態(tài)設(shè)置的。
這個(gè)搜索組件提供了一種靈活的方式來生成不同類型的表單項(xiàng),以便用戶輸入搜索條件。通過配置 props.option 數(shù)組,可以定制化生成所需的表單項(xiàng),并且支持不同的表單驗(yàn)證和交互方式。如果需要進(jìn)一步定制和擴(kuò)展,可以根據(jù)具體的需求修改和補(bǔ)充代碼。
到此這篇關(guān)于Element-plus封裝搜索組件的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Element-plus 搜索組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 基于Vue3和Element Plus實(shí)現(xiàn)可擴(kuò)展的表格組件
- Vue3+Element Plus實(shí)現(xiàn)自定義彈窗組件的全屏功能
- ElementPlus組件與圖標(biāo)按需自動(dòng)引入的實(shí)現(xiàn)方法
- vue3中ts語法使用element plus分頁組件警告錯(cuò)誤問題
- Vue3使用element-plus組件不顯示問題
- Vue3導(dǎo)入Elementplus時(shí)組件無法加載的情況及解決
- vue3使用Element-plus的el-pagination分頁組件時(shí)無法顯示中文
- vue3+elementplus基于el-table-v2封裝公用table組件詳細(xì)代碼
相關(guān)文章
mini-vue渲染的簡(jiǎn)易實(shí)現(xiàn)
本文主要介紹了mini-vue渲染的簡(jiǎn)易實(shí)現(xiàn),主要簡(jiǎn)單來實(shí)現(xiàn)一個(gè)虛擬dom渲染真實(shí)dom,以及更新的方法。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08Vue 框架之鍵盤事件、健值修飾符、雙向數(shù)據(jù)綁定
這篇文章主要介紹了Vue 框架之鍵盤事件、健值修飾符、雙向數(shù)據(jù)綁定問題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11vue等待數(shù)據(jù)渲染完成后執(zhí)行下一個(gè)方法問題
這篇文章主要介紹了vue等待數(shù)據(jù)渲染完成后執(zhí)行下一個(gè)方法問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12有關(guān)vue 開發(fā)釘釘 H5 微應(yīng)用 dd.ready() 不執(zhí)行問題及快速解決方案
這篇文章主要介紹了有關(guān)vue 開發(fā)的釘釘 H5 微應(yīng)用 dd.ready() 不執(zhí)行問題及快速解決方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05