如何一步步基于element-ui封裝查詢組件
功能
接著前一篇文章基于element-ui框架封裝一個更好用的表格組件,我們開始寫查詢組件. 查詢組件的話,需要有什么呢? 下面我畫了一個粗略的原型,基本描述了查詢組件需要實現(xiàn)的功能了。
基本的查詢功能 [輸入條件,選擇下拉數(shù)據(jù)點擊查詢]
添加查詢下拉面板 [很多查詢的話,一行放不下,需要給一下更多查詢下拉面板,除掉默認(rèn)查詢的條件,都放下拉面板去,具體默認(rèn)查詢條件放幾個,可以做成通過傳入的參數(shù)控制]
添加條件展示 [如果有更多查詢面板的話,不打開查詢面板,根本不清楚數(shù)據(jù)是根據(jù)哪些條件查出來的.]
添加功能按鈕區(qū) [一般放置增刪改等批量操作的功能按鈕]
基本的查詢功能
查詢功能的話,需要搭配element-ui的輸入框、下拉框、日期框等組件去實現(xiàn),而用我們的組件的話,希望用一個數(shù)組去描述這個查詢條件,然后組件根據(jù)數(shù)組去把條件給識別并渲染出來. 首先,我們得先定義一下這個數(shù)組到底長什么樣.
// methods initSearch () { const searchArr = this.searchArr if (searchArr && searchArr.length) { let searchForm = {} searchArr.forEach((val) => { searchForm[val.key] = '' }) this.searchForm = searchForm } } // mounted mounted () { this.initSearch() },
目前最基本的就是這三個字段了,字段意義在代碼注釋已經(jīng)寫明白了。 后邊這個對象里邊可能還會擴(kuò)展一些其他的字段. 比如下拉數(shù)據(jù), 它一般都是通過后臺接口給到前端, 我們可以在這里添加一個url、param、method來去定義接口的名稱、參數(shù)和請求方式. 當(dāng)然后面實現(xiàn)的時候,我們再說. 那么我們開始開發(fā)吧.
查詢條件初始化
外部我們傳入了searchArr數(shù)據(jù)后,我們得先在mounted的時候,遍歷它,給內(nèi)容定義的查詢條件searchForm設(shè)置響應(yīng)式數(shù)據(jù).
// methods initSearch () { const searchArr = this.searchArr if (searchArr && searchArr.length) { let searchForm = {} searchArr.forEach((val) => { searchForm[val.key] = '' }) this.searchForm = searchForm } } // mounted mounted () { this.initSearch() },
渲染頁面
因為我們設(shè)計的查詢頁面是兩部分的,包括展示在外邊的查詢條件和展示在更多的查詢條件. 這樣的話,如果我們在template里邊把結(jié)構(gòu)定義好的話,會發(fā)現(xiàn),兩遍需要寫重復(fù)的代碼。 如果后邊擴(kuò)展新功能上去的話,又是一個不好受的體力活了. 所以我們這邊就在render函數(shù)里邊寫jsx,這樣可以讓結(jié)構(gòu)更靈活,代碼可以更好的復(fù)用. 思路說完了,那么開始寫代碼了
// props 傳入一個searchArr數(shù)組描述查詢條件都有哪一些 /** * 查詢條件描述 */ searchArr: { type: Array, default: () => [] }, // data 定義兩個變量,searchForm:傳遞到后臺的查詢字段,selectOption:下拉數(shù)據(jù) searchForm: {}, selectOption: {} // mounted 初始化數(shù)據(jù) initSearch () { const searchArr = this.searchArr if (searchArr && searchArr.length) { let searchForm = {} searchArr.forEach((val) => { searchForm[val.__key] = '' // 把用戶定義的字段拿出來,放到searchForm里邊,再賦值一個空字符串 this.setOption(val) // 如果是下拉數(shù)據(jù)的話,去把下拉的列表賦值一下放到selectOption里邊 }) this.searchForm = searchForm } }, // methods 設(shè)置下拉數(shù)據(jù) async setOption (val) { if (~['select', 'mulSelect'].indexOf(val.__type)) { // 第一種情況如果下拉數(shù)據(jù)是本地寫死的 if (val.data && Array.isArray(val.data)) { this.$set(this.selectOption, val.__key, val.data) } else if (val.fetchUrl) { // 第二種情況,如果下拉數(shù)據(jù)是從后臺接口傳遞過來的數(shù)據(jù) const result = await request({ url: val.fetchUrl, method: val.method || 'post' }) if (result && result.data && result.data.success) { this.$set(this.selectOption, val.__key, result.data.list || []) } } } },
好的,我們完成初始化工作后,就可以在進(jìn)行渲染工作了,前面我們說到,我們采用render函數(shù)里邊寫jsx會更靈活一點,那么我們開始寫這部分的工作吧.
// render 函數(shù) <div class="searchTable"> <el-form inline={true} props={{model: this.searchForm}} ref="searchForm" class="searchForm"> { searchArr.map((o) => { return components[o.__type] ? components[o.__type](h, o, this) : '' }) } <el-form-item> <el-button size="small" type="primary" icon="el-icon-search" on-click={ queryTable }>查詢</el-button> <el-form-item> <el-link style="margin-left: 10px;" type="primary" underline={ false } on-click={ () => { this.moreShow = !this.moreShow } }>更多查詢 </el-link> </el-form-item> </el-form-item> <div class="more-search animated" class={ this.moreShow ? 'fadeInDown' : 'fadeOutUp' } v-show={ this.moreShow }> // ...更多查詢 </div> </el-form> </div>
在jsx中操作有個地方值得注意一下,在最新的vue-cli中,是已經(jīng)支持了在jsx中使用v-model等指令,但是element-ui中的el-input使用會報錯,下方使用的v-show指令就可以用的. 但是其實v-model也只是一個語法糖而已,我們這里就使用input事件拿到最新的輸入數(shù)據(jù),然后再賦值給searchForm. 這段代碼是抽離出來了的,因為外部顯示的查詢條件和收縮的查詢條件都得用到它,抽離出來后,就不用寫重復(fù)的邏輯了。 以下是抽離的components組件
/** * 輸入框 為什么抽離? 這里我們只是做表格查詢需要。 * 其實如果要封裝彈窗框那種表單提交組件。 也可以復(fù)用這里的邏輯 * @param { Function } h vue提供的h函數(shù) * @param { Object } item 用戶寫入的描述對象 * @param { Object } vm vue實例 */ export const input = function (h, item, vm) { const { searchForm } = vm return ( <el-form-item label={item.label}> <el-input size="small" on-input={(v) => { searchForm[item.__key] = v }} props={{ value: searchForm[item.__key], placeholder: "輸入內(nèi)容信息", ...item }} > </el-input> </el-form-item> ) } /** * * @param { Function } h vue提供的h函數(shù) * @param { Object } item 用戶寫入的描述對象 * @param { Object } vm vue實例 */ export const select = function (h, item, vm) { const { searchForm = {}, selectOption = {}, } = vm /** * 監(jiān)聽下拉改變事件 * @param { String | Number | Boolean } value 選擇下拉數(shù)據(jù)的值 * @param { Object } value */ const selectChange = function (value, item) { searchForm[item.__key] = value vm.$emit('on-change', value) } return ( <el-form-item label={item.label}> <el-select props={{ value: searchForm[item.__key], placeholder: "===請選擇===", filterable: true, clearable: true, ...item }} on-change={ (val) => { selectChange(val, item) } } size="small"> { selectOption[item.__key] && selectOption[item.__key].map((o) => { return ( <el-option key={ o.value } label={ o.text } value={ o.value } > </el-option> ) }) } </el-select> </el-form-item> ) } /** * * 多選的下拉框,可以再單選基礎(chǔ)上加上兩個屬性,寫一個這個方法,用戶可以稍微少些幾個單詞. * @param { Function } h vue提供的h函數(shù) * @param { Object } item 用戶寫入的描述對象 * @param { Object } vm vue實例 */ export const mulSelect = function (h, item, vm) { item['multiple'] = true item['collapse-tags'] = true return select(h, item, vm) }
以上其實就是把element的幾個組件抽離成獨立的方法,我們在外邊組件使用的時候,就能直接根據(jù)用戶傳遞的type,匹配到對應(yīng)的組件渲染了。 并且后邊要增加新的查詢組件的話,只要在這里擴(kuò)展就行,不用修改其他地方。 更容易維護(hù)。 另外我們在組件內(nèi)部默認(rèn)一些行為,比如默認(rèn)下拉可以搜索,默認(rèn)有清空按鈕等,如果系統(tǒng)不需要的話,也可在searchArr去寫上對應(yīng)屬性覆蓋.
return components[o.__type] ? components[o.__type](h, o, this) : ''
基本的渲染就寫好了, 然后我們在外邊使用組件
searchArr: [ { __type: 'input', label: '姓名', __key: 'name' }, { __type: 'select', label: '性別', __key: 'sex', fetchUrl: '/getSelect', method: 'post' // data: [ // { text: '男', value: '1' }, // { text: '女', value: '0' }, // ] }, ]
寫上對應(yīng)的mock數(shù)據(jù)
Mock.mock(/\/getSelect/, 'post', () => { return { status: 200, success: true, message: '獲取成功', list: [ { text: '男人', value: '1' }, { text: '女人', value: '2' } ], total: data.list.length } })
ok, 以上就簡單的把條件通過數(shù)組描述的方式渲染出來了。 接下來,我們得做更多查詢面板的查詢條件.
更多查詢以及展示優(yōu)化
外邊展示的查詢條件按用戶給的數(shù)量展示多少個,更多查詢也可按照用戶給定的參數(shù)展示一行展示多少個
=============== props =============== /** * 外部查詢條件展示多少個 */ frontCount: { type: Number, default: 2 }, /** * 更多查詢條件展示多少個 */ backCount: { type: Number, default: 3 } =============== end props =============== =============== computed =============== // 顯示在頁面上的查詢條件 frontSearchArr: function () { return this.searchArr.slice(0, this.frontCount) }, // 顯示在更多查詢里邊的條件 backSearchArr: function () { return this.searchArr.slice(this.frontCount, this.searchArr.length) }, // 返回寬度 getSpan: function () { const yu = this.backSearchArr.length % this.backCount // 余數(shù) const duan = 24 / this.backCount // 每個條件的寬度 if (yu === 0) { return 24 } else { return 24 - duan } } =============== end computed =============== ============== render ============== <div style={`width: ${this.backCount * 265 + 30}px`} class={ this.moreShow ? `${className} fadeInDown` : `${className} fadeOutUp` } v-show={ this.moreShow }> <el-row> { backSearchArr.map((o) => { return ( <el-col span={24 / this.backCount}> { components[o.__type] ? components[o.__type](h, o, this) : '' } </el-col> ) }) } <el-col class="searchBtn" span={ getSpan } > <el-button size="small" type="primary" icon="el-icon-search" on-click={ queryTable }>查詢</el-button> <el-button size="small" type="default" icon="el-icon-upload2" on-click={ () => { this.moreShow = !this.moreShow } }>收縮</el-button> </el-col> </el-row> </div> ============== end render ==============
在計算屬性里邊設(shè)置外部顯示的數(shù)據(jù)和內(nèi)部顯示的數(shù)據(jù)。 把數(shù)據(jù)切割成兩部分. 搜索組件固定長度265. 然后根據(jù)條件計算出來更多查詢面板的寬度,再根據(jù)getSpan計算屬性算每行數(shù)據(jù)給的span寬度是多少. 查詢和收縮按鈕根據(jù)查詢條件的多少,去計算它放哪個位置。
經(jīng)測試,效果基本上符合要求.
下拉組件聯(lián)動查詢
比如我們需要用我們的查詢組件寫三級聯(lián)動的話,應(yīng)該怎么實現(xiàn)呢?
選擇省后,需要根據(jù)選擇的省加載對應(yīng)的市下拉數(shù)據(jù)
如果刪除省后,需要把對應(yīng)的省和市的下拉也清空掉
聯(lián)動主要就是做好這兩塊功能。 一塊是加載對應(yīng)數(shù)據(jù),一塊清空對應(yīng)數(shù)據(jù). ok, 清楚需要做什么了,我們就開始著手怎么做吧.
{ __type: 'select', __key: 'province', __fetchUrl: '/getProvince', __method: 'get', __nextKey: 'city', __nextFetch: '/getCity', __nextParam: ['province'], __nextMethod: 'get', __emptyArr: ['city', 'district'], label: '省', labelWidth: '40px', }, { __type: 'select', __key: 'city', __method: 'get', __nextKey: 'district', __nextFetch: '/getDistrict', __nextParam: ['province', 'city'], __nextMethod: 'get', __emptyArr: ['district'], label: '市', labelWidth: '40px', }, { __type: 'select', __key: 'district', label: '區(qū)', labelWidth: '40px', }
其中獲取省的數(shù)據(jù),其實只要寫__fetchUrl: '/getProvince'和__method: 'get'定義請求接口就可以拿到下拉數(shù)據(jù)了。 然后選擇省下拉的時候,需要在change事件里邊去根據(jù)選擇的省請求數(shù)據(jù). 這時候我們需要在數(shù)據(jù)里邊定義一下change的時候,需要做的事情. 下面看一下代碼:
__nextKey: 'city', // 給哪一個下拉數(shù)據(jù)賦值 __nextFetch: '/getCity', // 賦值的請求接口 __nextParam: ['province'], // 請求參數(shù) __nextMethod: 'get', // 請求方式 __emptyArr: ['city', 'district'] // 當(dāng)修改了下拉數(shù)據(jù)的時候,需要清空哪些下拉類別的值 /** * 監(jiān)聽下拉改變事件 * @param { String | Number | Boolean } value 選擇下拉數(shù)據(jù)的值 * @param { Object } value */ const selectChange = async function (value, item) { searchForm[item.__key] = value // 置空下拉列表和下拉數(shù)據(jù) if (item && item.__emptyArr) { for (let i = 0; i < item.__emptyArr.length; i++) { let key = item.__emptyArr[i] if (selectOption[key]) { selectOption[key] = [] } vm.$set(searchForm, key, '') } } if (item && item.__nextFetch && item.__nextParam) { let param = {} for (let j = 0; j < item.__nextParam.length; j++) { let value = searchForm[item.__nextParam[j]] if (value) { param[item.__nextParam[j]] = value } } const res = await request({ url: item.__nextFetch, method: item.__nextMethod || 'post' }, param) if (res) { let { data } = res if (data && data.success) { vm.$set(selectOption, item.__nextKey, data.list) } } } vm.$emit('on-change', value) }
ok, 這樣基本就完成了三級聯(lián)動的功能了。 以下是效果圖
組件擴(kuò)展
組件擴(kuò)展就很簡單了,如果我們還缺少一些需要的組件,可以直接在components.js里邊新增一個
/** * 日期選擇器 * @param {*} h * @param {*} item * @param {*} vm */ export const date = function (h, item, vm) { const { searchForm } = vm return ( <el-form-item label={item.label} labelWidth={item.labelWidth}> <el-date-picker on-input={(v) => { searchForm[item.__key] = v }} props={{ type: 'date', size: 'small', value: searchForm[item.__key], placeholder: "選擇日期", ...item }} > </el-date-picker> </el-form-item> ) } // searchArr使用的時候添加 { __type: 'date', __key: 'birthday', label: '生日' }
其他地方都不用修改,就完成了一個新查詢條件類型的添加了. 以下是效果
搜索條件展示
搜索條件功能比較簡單,就是在用戶點擊搜索后,顯示輸入的條件. 我們把這個分離出來作為一個獨立的小組件
<query-info ref="queryInfo" on-remove={this.removeTag} selectionOption={ this.selectOption }></query-info>
這個組件傳入下拉數(shù)組過去,我們在設(shè)計的時候,有說過,他還可以點擊叉刪除條件,所以得拋出來一個remove自定義事件,然后刪除掉條件后,我們把這個條件清空,然后調(diào)用查詢方法. 查詢的時候,再去把搜索條件傳遞給組件,讓組件顯示出來。 這里需要注意,我們不能直接把搜索條件通過props傳入到組件里邊,如果是這樣傳入的話,由于數(shù)據(jù)是響應(yīng)式的,在你輸入條件的時候,下方就會顯示出來,這并不是我們想要的效果,我們想要的效果是在點擊查詢按鈕的時候,展示正在查詢的查詢條件. 所以這里的話,我們就在查詢按鈕方法里邊,直接去調(diào)用它內(nèi)部的方法把條件傳入過去,然后內(nèi)部再利用JSON.parse(JSON.stringify(val))的方式,實現(xiàn)一個深拷貝,讓數(shù)據(jù)和外部數(shù)據(jù)隔離開.
removeTag (name) { this.searchForm[name] = '' this.queryTable() } // 查詢 queryTable () { this.$refs.queryInfo && this.$refs.queryInfo.queryTable(this.searchForm) this.moreShow = false this.$emit('search', this.searchForm) }
組件的實現(xiàn)如下:
<template> <div class="queryInfo"> <el-tag v-for="tag in tags" :key="tag" closable style="margin-right: 10px;" @close="close(tag)" > {{displayName(tag)}} </el-tag> </div> </template> <script> import { getTitle } from '@/utils/utils' export default { props: { // 下拉數(shù)據(jù) selectionOption: { type: Object, default: () => [] } }, data () { return { // 查詢條件 searchForm: {} } }, computed: { /// 計算出輸入的條件的數(shù)組 tags: function () { let tags = [] let keys = Object.keys(this.searchForm) keys.forEach((key) => { if (this.searchForm[key]) { tags.push(key) } }) return tags } }, methods: { // 點擊關(guān)閉 close (tag) { this.searchForm[tag] = '' this.$emit('remove', tag) }, // 外部調(diào)用的方法 queryTable (searchForm) { this.searchForm = JSON.parse(JSON.stringify(searchForm)) }, // 顯示名稱, 如果是下拉數(shù)據(jù),則去下拉數(shù)據(jù)里邊匹配到名稱顯示出來 displayName(key) { let value = this.searchForm[key] if (this.selectionOption[key]) { return getTitle(value, this.selectionOption[key]) } else { return value } } } } </script> <style scoped> .queryInfo { margin-bottom: 10px; } </style>
實現(xiàn)的效果如下:
添加功能按鈕區(qū)
這個目前就在searchTable組件用插槽的方式加入
<div class="right"> {this.$slots.rightButton} </div> // 使用 <search-table :searchArr="searchArr" @search="search"> <template v-slot:rightButton> <el-button size="small" type="success" @click="() => { MessageBox({ message: '開發(fā)中', type: 'warning' })}"> 添加 </el-button> </template> </search-table> 復(fù)制代碼
效果:
寫在最后
那么至此,查詢組件想要實現(xiàn)的一些功能都一一實現(xiàn)好了. 其實做起來并不復(fù)雜,最重要的點不在于做,而在于做之前要想清楚. 后面我們再把動態(tài)表頭給做好,那么大致關(guān)于表格這塊的相關(guān)需求就完成了.
也可以給我們的開源項目點點star: http://github.crmeb.net/u/defu 不勝感激 !
到此這篇關(guān)于如何一步步基于element-ui封裝查詢組件的文章就介紹到這了,更多相關(guān)element-ui封裝查詢組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue mixin實現(xiàn)組件功能復(fù)用示例詳解
這篇文章主要為大家介紹了Vue mixin實現(xiàn)組件功能復(fù)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10vue主動刷新頁面及列表數(shù)據(jù)刪除后的刷新實例
今天小編就為大家分享一篇vue主動刷新頁面及列表數(shù)據(jù)刪除后的刷新實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09ant design vue導(dǎo)航菜單與路由配置操作
這篇文章主要介紹了ant design vue導(dǎo)航菜單與路由配置操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Pinia入門學(xué)習(xí)之實現(xiàn)簡單的用戶狀態(tài)管理
Vue3雖然相對于Vue2很多東西都變了,但是核心的東西還是沒有變,比如說狀態(tài)管理、路由等,再Vue3中尤大神推薦我們使用pinia來實現(xiàn)狀態(tài)管理,他也說pinia就是Vuex的新版本,這篇文章主要給大家介紹了關(guān)于Pinia入門學(xué)習(xí)之實現(xiàn)簡單的用戶狀態(tài)管理的相關(guān)資料,需要的朋友可以參考下2022-11-11