前情回顧:
封裝第三方組件(11)基于element-plus封裝一個(gè)基于json動(dòng)態(tài)渲染的表單控件
功能
使用 vue3 + element-plus 封裝了一個(gè)查詢控件,專為管理后臺(tái)量身打造,支持各種查詢需求:
- 多種查詢方式
- 快捷查詢
- 更多查詢
- 自定義查詢
- 支持防抖
- 清空每個(gè)查詢條件
- 依賴 json 動(dòng)態(tài)創(chuàng)建
有些控件自帶清空功能,有些沒有自帶清空功能,那么就需求我們手動(dòng)加上清空的功能。
技術(shù)棧
- Vite2
- Vue3
- element-plus
查詢控件設(shè)計(jì)
【自我感覺良好的一個(gè)腦圖】

在線演示
https://naturefw.gitee.io/nf-vue-cdn/elecontrol/
進(jìn)入頁面后請(qǐng)點(diǎn)擊“查詢控件”。
可以在“表單控件”里面添加測(cè)試數(shù)據(jù),數(shù)據(jù)會(huì)存入webSQL。
受限于webSQL,有些查詢功能無法實(shí)現(xiàn)。
各種查詢方式
查詢控件針對(duì)不同的數(shù)據(jù)類型(后端數(shù)據(jù)庫字段類型),量身打造了多種查詢方式,讓查詢更便捷!
文本

針對(duì)文本類的數(shù)據(jù)類型(varchar、text等),提供常用的模糊查詢(包含)、精確查詢(=),還有起始于、結(jié)束于等查詢方式可供選擇。
這樣用戶可以更靈活方便的進(jìn)行查詢操作。
數(shù)字

針對(duì)數(shù)值類型(int、float、decme等),提供常用的精確查詢(=)、范圍查詢(從xx到xxx)還有大于等于等查詢方式。
單選組的查詢


針對(duì)枚舉類型,int 或者 varchar 等有限數(shù)量。
單選組有兩種情況,一個(gè)是常見的查詢一種情況即可,選擇第一選項(xiàng)那么只需要顯示第一個(gè)選項(xiàng)對(duì)應(yīng)的數(shù)據(jù)。
另一個(gè)就是想同時(shí)看多個(gè)選項(xiàng)的結(jié)果,那么這時(shí)候還用單選組的方式就不適合了,需要變成多選組的方式,這樣才可以讓用戶選擇多個(gè)選項(xiàng)。
所以這里的單選的查詢支持兩種查詢方式:
- =: 只能查詢一個(gè)選項(xiàng),對(duì)應(yīng)單選。
- 包含:可以同時(shí)查詢多個(gè)選項(xiàng),對(duì)應(yīng)多選。
支持清空查詢條件,即點(diǎn)擊右側(cè)的“x”。
多選支持防抖。
勾選和開關(guān)

二者對(duì)應(yīng)的數(shù)據(jù)類型是 bool 型的(bit),所以只有“=”這一種查詢方式,增加了一個(gè)“清空”的按鈕,這樣可以單獨(dú)清掉查詢條件。
級(jí)聯(lián)選擇

常見的級(jí)聯(lián)選擇是省市區(qū)縣的選擇,組件默認(rèn)給的model是一個(gè)數(shù)組形式,有多少級(jí)就會(huì)有多少個(gè)數(shù)組。
但是在后端數(shù)據(jù)庫里面,往往會(huì)分成多個(gè)字段來存放,比如省份用一個(gè)字段表示,城市用一個(gè)字段表示,區(qū)縣又是一個(gè)字段表示。
那么我們?cè)诓樵兊臅r(shí)候,就需要把查詢結(jié)果按照字段給拆分開,這樣才便于查詢。
所以這里把查詢結(jié)果按照字段拆分開然后在返回給后端,比如這樣:
{ "a": [ 401, "zhinan" ], "b": [ 401, "shejiyuanze" ], "c": [ 401, "yizhi" ] }
日期
日期查詢比較復(fù)雜,這里對(duì)應(yīng)的數(shù)據(jù)類型是date,選擇后返回的數(shù)據(jù)是“2021-05-20”的形式。
然后就是如何讓用戶感覺爽的問題了。
- 常規(guī)查詢方式

一般都是如上圖所示,直接選擇日期范圍,這個(gè)看起來似乎沒有啥問題,可以選擇任意日期。
但是如果用戶想查詢2021年1月到2021年3月的數(shù)據(jù),那么用戶的操作就會(huì)比較繁瑣。
我們來看看一共要點(diǎn)擊幾次鼠標(biāo)?
打開日期欄 》 找到一月份(n次) 》 選擇一號(hào) 》 找到三月份(又是n次) 》選擇31號(hào)。
整個(gè)流程需要點(diǎn)好多次鼠標(biāo),實(shí)在是太麻煩了。
- 通過月份查詢?nèi)掌诜秶?br> 如果可以直接選擇月份呢?像這樣:

如果用戶想選擇多個(gè)月份的日期,可以通過“從” + “年月”的形式,選擇起始月份即可,返回的數(shù)據(jù)是"2021-01-01", "2021-03-31" 的形式。

如果客戶想選擇一個(gè)月的范圍,那么可以用“=” + “年月”的方式來選擇(如上圖),返回的數(shù)據(jù)是"2021-02-01", "2021-02-28" 的形式。
這樣用戶就非常方便了,節(jié)省了n次鼠標(biāo)點(diǎn)擊。不過這還沒有結(jié)束,還有選擇“年”的情況。
- 通過年查詢?nèi)掌诜秶?br> 如果要查詢一年的或者多年的日期范圍呢?我們可以選擇“年”的方式。

如果選擇一整年的話,我們可以使用“=” + “年”的方式(如上圖),選擇需要的年份即可,返回的數(shù)據(jù)是 "2021-01-01", "2021-12-31" 的形式。

如果選擇連續(xù)的多個(gè)年份,可以用“從” + “年”的方式(如上圖),選擇起始年份即可,返回的數(shù)據(jù)是"2021-01-01", "2022-12-31" 的形式。
年、年月、年周的查詢
上面是針對(duì)date類型的數(shù)據(jù),這里是針對(duì)int、varchar類型的數(shù)據(jù)。
有時(shí)候?yàn)榱思涌觳樵兯俣龋瑪?shù)據(jù)庫設(shè)計(jì)上面可能會(huì)用增加“冗余字段”的方式來提升性能,比如增加“年”的字段,類型是int,存放“2021”、“2022”這樣的數(shù)據(jù)。
同理,可以增加“年月”的字段,類型是int,存放“202101”、“202103”這類的數(shù)據(jù),還有“年周”的情況。
這里的查詢方式就是針對(duì)這種情況來設(shè)計(jì)的。
- 年的查詢


要比日期查詢簡單很多。
- 年月的查詢


- 年周的查詢
這里不是指星期幾,而是一年內(nèi)的第幾周,聽說有些企業(yè)是按照周來安排工作的,所以這里也提供了周的查詢。


日期時(shí)間的查詢

快速查詢
顯示常用的查詢條件。

自定義查詢方案
可以把常用的查詢字段放在一起,組成一個(gè)查詢方案,方便用戶使用。

更多查詢
顯示全部查詢條件,查詢后的字段可以帶入快捷查詢,便于隨時(shí)更改查詢條件。


文件結(jié)構(gòu)
上面都是介紹功能,下面開始介紹一下實(shí)現(xiàn)方式。
首先看一下文件結(jié)構(gòu):

packages
存放基礎(chǔ)的js,和UI庫無關(guān)的基本邏輯代碼,很顯然等穩(wěn)定后會(huì)發(fā)布到npm上面,以便于支持其他UI庫。
目前有表單子控件、表單控件、查詢子控件、查詢控件,以后還會(huì)有列表控件、按鈕控件等。control-web
web 控件的意思。存放組件的UI部分。至于會(huì)不會(huì)發(fā)布到npm,目前還沒有想好,因?yàn)橛袀€(gè)靈活性的問題。views
這里就是如何使用的代碼了。
實(shí)現(xiàn)方式
我們以文本類的查詢?yōu)槔M(jìn)行介紹,我們先做一個(gè)查詢方式的組件,然后做一個(gè)文本的查詢子控件。
查詢方式
<template>
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
{{kindName}}<i
class=" el-icon--right"
:class="{'el-icon-arrow-down': isUp, 'el-icon-arrow-up': !isUp}"></i>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="kindId in findKind"
:key="'s_kind_'+ kindId"
:command="kindId"
>
{{findKindList[kindId].name}}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
使用 el-dropdown 做一個(gè)選擇列表。
import { defineComponent, ref } from 'vue'
import { findKindList } from '/nf-control-web'
// 查詢方式的 select
export default defineComponent({
name: 'el-find-kind',
props: {
// 返回選擇的查詢方式
modelValue: [Number],
// 需要顯示的查詢方式
findKind: {
type: Array,
default: () => { return [411] }
}
},
emits: ['update:modelValue', 'change'],
setup (props, context) {
const kindName = ref(findKindList[props.modelValue].name)
const handleCommand = (command) => {
kindName.value = findKindList[command].name
context.emit('update:modelValue', command)
context.emit('change', command)
}
return {
isUp,
kindName,
findKindList,
handleCommand
}
}
})
設(shè)置屬性,接收查詢方式,和用戶選擇的查詢方式。
查詢子控件
<template>
<!--查詢方式-->
<div style="float:left;width:65px;text-align:center;">
<find-kind
v-model="findChoiceKind"
:findKind="findKind"
@change="myChange"
/>
</div>
<!--查詢內(nèi)容-->
<div :style="{width: (150 * colCount - 10 ) + 'px'}" style="float:left;">
<div style="float:left;" :style="{width: (150 * colCount - 40 ) + 'px'}">
<component
:is="ctlList[controlType]"
v-model="findText"
v-bind="$attrs"
:delay="delay"
:colName="colName"
@myChange="myChange">
</component>
</div>
</div>
</template>
放置查詢方式和查詢用的組件。
import { defineComponent } from 'vue'
// 引入查詢子控件的管理類
import { findItemManage } from '/nf-control-web'
// 查詢方式的控件
import selectFindKind from './s-findkind.vue'
// 異步組件,引入表單子控件
import { formItemToFindItem } from '../nf-el-find-item/map-el-find-item.js'
/*
* 查詢子控件,文本類
* * 單行文本
* * 多行文本
* * ulr、電話、郵箱等
*/
export default defineComponent({
name: 'el-find-item-text',
inheritAttrs: false,
props: {
controlId: Number, // 控件ID
controlType: Number, // 控件類型
colName: String, // 字段名稱
modelValue: [Array, String], // 查詢結(jié)果,數(shù)組形式
colCount: { // 占用空間
type: Number,
default: 1
},
findKind: { // 查詢方式
type: Array, // , 407, 408
default: () => { return [403, 401, 402, 404, 405, 406] }
},
delay: { // 防抖
type: Number,
default: 600
}
},
components: {
'find-kind': selectFindKind
},
emits: ['update:modelValue', 'my-change'],
setup (props, context) {
// 表單子控件 to 查詢子控件 的 字典
const ctlList = formItemToFindItem
const {
findChoiceKind, // 選擇的查詢方式
findText, // 一個(gè)關(guān)鍵字查詢
mySubmit
} = findItemManage(props, context)
// 設(shè)置默認(rèn)查詢方式
findChoiceKind.value = props.findKind[0]
// 提交查詢結(jié)果
const myChange = () => {
// 一個(gè)關(guān)鍵字查詢
mySubmit(findText.value)
}
return {
ctlList, // 控件字典,用于加載具體的控件
findChoiceKind, // 查詢方式
findText, // 一個(gè)查詢關(guān)鍵字
myChange // 觸發(fā)提交事件
}
}
})
設(shè)置需要的屬性,比如具體的查詢方式、防抖時(shí)間間隔等。因?yàn)槲谋静樵儽容^簡單,所以只需要簡單的提交查詢條件即可。
查詢控件
<template>
<!--快捷查詢-->
<el-card class="box-card">
<el-scrollbar>
<div class="flex-content" style="min-width:400px;">
<el-form
inline
label-position="right"
:model="findItemModel"
ref="formControl"
class="demo-form-expand"
label-width="1px"
size="mini"
>
<el-form-item style="width:100px">
<el-dropdown size="small">
<el-button type="primary">
快捷查詢<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
@click="quickClick(0)"
>
快捷查詢
</el-dropdown-item>
<el-dropdown-item
v-for="(item, key, index) in customer"
:key="'quick_' + index"
@click="quickClick(key)"
>
{{item.title}}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-form-item>
<el-form-item
v-for="(ctrId, index) in arrQuickFind"
:key="'find_quick_'+index"
style="border:1px solid #cfe1f3;min-height:33px;"
:style="{width: ( 160 * getCtrMeta(ctrId).colCount + 80) + 'px'}"
>
<!--判斷要不要加載插槽-->
<template v-if="getCtrMeta(ctrId).controlType === 1">
<slot :name="ctrId">父組件沒有設(shè)置插槽</slot>
</template>
<!--查詢的子控件,采用動(dòng)態(tài)組件的方式-->
<template v-else>
<component
:is="ctlList[getCtrMeta(ctrId).controlType]"
v-model="findItemModel[ctrId]"
v-bind="getCtrMeta(ctrId)"
@myChange="mySubmit">
</component>
</template>
</el-form-item>
<el-form-item style="width:60px">
<el-button type="primary" round @click="moreOpen">更多</el-button>
</el-form-item>
</el-form>
</div>
</el-scrollbar>
</el-card>
<!--更多查詢,放在抽屜里面-->
<findmore
:allFind="allFind"
:reload="reload"
:itemMeta="itemMeta"
:findKind="findKind"
:moreFind="moreFind"
v-model:isShow="isShow"
/>
</template>
這里是快捷查詢,更多查詢做成了單獨(dú)的組件,這樣可以讓模板代碼簡潔一點(diǎn),不至于太亂。
/**
* @function div 格式的查詢控件
* @description 可以依據(jù) json 動(dòng)態(tài)生成查詢控件
* @returns {*} Vue 組件,查詢控件
*/
export default {
name: 'el-find-div',
components: {
findmore
},
props: {
...findProps
},
setup (props, context) {
// 控件字典
const ctlList = findItemListKey
// 依據(jù)ID獲取組件的meta,因?yàn)?model 不支持[]嵌套
const getCtrMeta = (id) => {
return props.itemMeta[id] || {}
}
const {
moreFind, // 接收更多查詢 更多查詢里面子控件的事件
isShow, // 抽屜是否打開
arrQuickFind, // 快捷欄的數(shù)組
findItemModel, // 查詢子控件的model
moreOpen, // 點(diǎn)擊更多,清空快捷
quickClick, // 個(gè)性化方案的單擊事件
mySubmit // 查詢子控件的事件
} = findManage(props, context)
return {
isShow, // 抽屜是否打開
moreFind, // 接收更多查詢
arrQuickFind, // 快捷欄的數(shù)組
ctlList, // 子控件字典
resetForm, // 重置表單
formControl, // 獲取表單的dom
getCtrMeta, // 返回子控件的meta
findItemModel, // 查詢子控件的model
moreOpen, // 點(diǎn)擊更多,清空快捷
quickClick, // 個(gè)性化方案的單擊事件
mySubmit
}
}
}
代碼比較多,這里是 setup 部分,主要負(fù)責(zé)代碼函數(shù)的整合。減少代碼混亂的程度。
使用方式
<template>
<!--演示查詢控件-->
<nf-find
v-model="query"
v-bind="formProps"
/>
查詢條件:{{query}}
<!--數(shù)據(jù)列表 演示查詢結(jié)果-->
<findGrid :dataList="dataList"/>
<!--可以分頁-->
<findPager/>
</template>
模板部分比較簡單了,設(shè)置好屬性即可。
import { reactive, watch } from 'vue'
// 組件
import findCom from '../control-web/nf-el-find/el-find-div.vue'
import findGrid from './find-grid.vue'
import findPager from './find-pager.vue'
// 加載json文件
import json from '/json/find-test.json'
// 數(shù)據(jù)列表的狀態(tài)
import dataListControl from '../control/data-list'
export default {
name: 'eleFindComponent',
components: {
findGrid,
findPager,
'nf-find': findCom
},
setup () {
const query = reactive({})
const findTest = json.findTest
// 設(shè)置查詢控件的屬性
const findProps = reactive({})
// 添加重新綁定的開關(guān)
findProps.reload = false
// 模擬異步加載meta
Object.assign(findProps, findTest.baseProps)
findProps.itemMeta = findTest.itemMeta // 表單子控件的屬性
findProps.findKind = findTest.findKind // 查詢方式
return {
query,
dataList,
// 渲染表單的meta
findProps
}
}
}
這里主要是加載json文件,然后給查詢控件設(shè)置屬性。
然后獲得查詢條件,提交給后端API申請(qǐng)數(shù)據(jù)即可。
json 文件的格式
比較長,發(fā)個(gè)圖片示意一下:

更多代碼歡迎查看源碼。
源碼
https://gitee.com/naturefw/nf-vite2-element