前情回顧
基於 el-form 封裝一個依賴 json 動態渲染的表單控件
Vue3 封裝第三方組件(一)做一個合格的傳聲筒
功能
使用 vue3 + element-plus 封裝了一個查詢控件,專為管理后台量身打造,支持各種查詢需求:
- 多種查詢方式
- 快捷查詢
- 更多查詢
- 自定義查詢
- 支持防抖
- 清空每個查詢條件
- 依賴 json 動態創建
有些控件自帶清空功能,有些沒有自帶清空功能,那么就需求我們手動加上清空的功能。
技術棧
- Vite2
- Vue3
- element-plus
查詢控件設計
【自我感覺良好的一個腦圖】
在線演示
https://naturefw.gitee.io/nf-vue-cdn/elecontrol/
進入頁面后請點擊“查詢控件”。
可以在“表單控件”里面添加測試數據,數據會存入webSQL。
受限於webSQL,有些查詢功能無法實現。
功能演示
查詢功能具體是什么樣子呢?我們先來看一段視頻:
點擊查看視頻演示
各種查詢方式
查詢控件針對不同的數據類型(后端數據庫字段類型),量身打造了多種查詢方式,讓查詢更便捷!
文本
針對文本類的數據類型(varchar、text等),提供常用的模糊查詢(包含)、精確查詢(=),還有起始於、結束於等查詢方式可供選擇。
這樣用戶可以更靈活方便的進行查詢操作。
數字
針對數值類型(int、float、decme等),提供常用的精確查詢(=)、范圍查詢(從xx到xxx)還有大於等於等查詢方式。
單選組的查詢
針對枚舉類型,int 或者 varchar 等有限數量。
單選組有兩種情況,一個是常見的查詢一種情況即可,選擇第一選項那么只需要顯示第一個選項對應的數據。
另一個就是想同時看多個選項的結果,那么這時候還用單選組的方式就不適合了,需要變成多選組的方式,這樣才可以讓用戶選擇多個選項。
所以這里的單選的查詢支持兩種查詢方式:
- =: 只能查詢一個選項,對應單選。
- 包含:可以同時查詢多個選項,對應多選。
支持清空查詢條件,即點擊右側的“x”。
多選支持防抖。
勾選和開關
二者對應的數據類型是 bool 型的(bit),所以只有“=”這一種查詢方式,增加了一個“清空”的按鈕,這樣可以單獨清掉查詢條件。
級聯選擇
常見的級聯選擇是省市區縣的選擇,組件默認給的model是一個數組形式,有多少級就會有多少個數組。
但是在后端數據庫里面,往往會分成多個字段來存放,比如省份用一個字段表示,城市用一個字段表示,區縣又是一個字段表示。
那么我們在查詢的時候,就需要把查詢結果按照字段給拆分開,這樣才便於查詢。
所以這里把查詢結果按照字段拆分開然后在返回給后端,比如這樣:
日期
日期查詢比較復雜,這里對應的數據類型是date,選擇后返回的數據是“2021-05-20”的形式。
然后就是如何讓用戶感覺爽的問題了。
- 常規查詢方式
一般都是如上圖所示,直接選擇日期范圍,這個看起來似乎沒有啥問題,可以選擇任意日期。
但是如果用戶想查詢2021年1月到2021年3月的數據,那么用戶的操作就會比較繁瑣。
我們來看看一共要點擊幾次鼠標?
打開日期欄 》 找到一月份(n次) 》 選擇一號 》 找到三月份(又是n次) 》選擇31號。
整個流程需要點好多次鼠標,實在是太麻煩了。
- 通過月份查詢日期范圍
如果可以直接選擇月份呢?像這樣:
如果用戶想選擇多個月份的日期,可以通過“從” + “年月”的形式,選擇起始月份即可,返回的數據是"2021-01-01", "2021-03-31" 的形式。
如果客戶想選擇一個月的范圍,那么可以用“=” + “年月”的方式來選擇(如上圖),返回的數據是"2021-02-01", "2021-02-28" 的形式。
這樣用戶就非常方便了,節省了n次鼠標點擊。不過這還沒有結束,還有選擇“年”的情況。
- 通過年查詢日期范圍
如果要查詢一年的或者多年的日期范圍呢?我們可以選擇“年”的方式。
如果選擇一整年的話,我們可以使用“=” + “年”的方式(如上圖),選擇需要的年份即可,返回的數據是 "2021-01-01", "2021-12-31" 的形式。
如果選擇連續的多個年份,可以用“從” + “年”的方式(如上圖),選擇起始年份即可,返回的數據是"2021-01-01", "2022-12-31" 的形式。
年、年月、年周的查詢
上面是針對date類型的數據,這里是針對int、varchar類型的數據。
有時候為了加快查詢速度,數據庫設計上面可能會用增加“冗余字段”的方式來提升性能,比如增加“年”的字段,類型是int,存放“2021”、“2022”這樣的數據。
同理,可以增加“年月”的字段,類型是int,存放“202101”、“202103”這類的數據,還有“年周”的情況。
這里的查詢方式就是針對這種情況來設計的。
- 年的查詢
要比日期查詢簡單很多。
- 年月的查詢
- 年周的查詢
這里不是指星期幾,而是一年內的第幾周,聽說有些企業是按照周來安排工作的,所以這里也提供了周的查詢。
日期時間的查詢
快速查詢
顯示常用的查詢條件。
自定義查詢方案
可以把常用的查詢字段放在一起,組成一個查詢方案,方便用戶使用。
更多查詢
顯示全部查詢條件,查詢后的字段可以帶入快捷查詢,便於隨時更改查詢條件。
文件結構
上面都是介紹功能,下面開始介紹一下實現方式。
首先看一下文件結構:
-
packages
存放基礎的js,和UI庫無關的基本邏輯代碼,很顯然等穩定后會發布到npm上面,以便於支持其他UI庫。
目前有表單子控件、表單控件、查詢子控件、查詢控件,以后還會有列表控件、按鈕控件等。 -
control-web
web 控件的意思。存放組件的UI部分。至於會不會發布到npm,目前還沒有想好,因為有個靈活性的問題。 -
views
這里就是如何使用的代碼了。
實現方式
我們以文本類的查詢為例進行介紹,我們先做一個查詢方式的組件,然后做一個文本的查詢子控件。
查詢方式
<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 做一個選擇列表。
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
}
}
})
設置屬性,接收查詢方式,和用戶選擇的查詢方式。
查詢子控件
<template>
<!--查詢方式-->
<div style="float:left;width:65px;text-align:center;">
<find-kind
v-model="findChoiceKind"
:findKind="findKind"
@change="myChange"
/>
</div>
<!--查詢內容-->
<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], // 查詢結果,數組形式
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, // 一個關鍵字查詢
mySubmit
} = findItemManage(props, context)
// 設置默認查詢方式
findChoiceKind.value = props.findKind[0]
// 提交查詢結果
const myChange = () => {
// 一個關鍵字查詢
mySubmit(findText.value)
}
return {
ctlList, // 控件字典,用於加載具體的控件
findChoiceKind, // 查詢方式
findText, // 一個查詢關鍵字
myChange // 觸發提交事件
}
}
})
設置需要的屬性,比如具體的查詢方式、防抖時間間隔等。因為文本查詢比較簡單,所以只需要簡單的提交查詢條件即可。
查詢控件
<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">父組件沒有設置插槽</slot>
</template>
<!--查詢的子控件,采用動態組件的方式-->
<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>
這里是快捷查詢,更多查詢做成了單獨的組件,這樣可以讓模板代碼簡潔一點,不至於太亂。
/**
* @function div 格式的查詢控件
* @description 可以依據 json 動態生成查詢控件
* @returns {*} Vue 組件,查詢控件
*/
export default {
name: 'el-find-div',
components: {
findmore
},
props: {
...findProps
},
setup (props, context) {
// 控件字典
const ctlList = findItemListKey
// 依據ID獲取組件的meta,因為 model 不支持[]嵌套
const getCtrMeta = (id) => {
return props.itemMeta[id] || {}
}
const {
moreFind, // 接收更多查詢 更多查詢里面子控件的事件
isShow, // 抽屜是否打開
arrQuickFind, // 快捷欄的數組
findItemModel, // 查詢子控件的model
moreOpen, // 點擊更多,清空快捷
quickClick, // 個性化方案的單擊事件
mySubmit // 查詢子控件的事件
} = findManage(props, context)
return {
isShow, // 抽屜是否打開
moreFind, // 接收更多查詢
arrQuickFind, // 快捷欄的數組
ctlList, // 子控件字典
resetForm, // 重置表單
formControl, // 獲取表單的dom
getCtrMeta, // 返回子控件的meta
findItemModel, // 查詢子控件的model
moreOpen, // 點擊更多,清空快捷
quickClick, // 個性化方案的單擊事件
mySubmit
}
}
}
代碼比較多,這里是 setup 部分,主要負責代碼函數的整合。減少代碼混亂的程度。
使用方式
<template>
<!--演示查詢控件-->
<nf-find
v-model="query"
v-bind="formProps"
/>
查詢條件:{{query}}
<!--數據列表 演示查詢結果-->
<findGrid :dataList="dataList"/>
<!--可以分頁-->
<findPager/>
</template>
模板部分比較簡單了,設置好屬性即可。
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'
// 數據列表的狀態
import dataListControl from '../control/data-list'
export default {
name: 'eleFindComponent',
components: {
findGrid,
findPager,
'nf-find': findCom
},
setup () {
const query = reactive({})
const findTest = json.findTest
// 設置查詢控件的屬性
const findProps = reactive({})
// 添加重新綁定的開關
findProps.reload = false
// 模擬異步加載meta
Object.assign(findProps, findTest.baseProps)
findProps.itemMeta = findTest.itemMeta // 表單子控件的屬性
findProps.findKind = findTest.findKind // 查詢方式
return {
query,
dataList,
// 渲染表單的meta
findProps
}
}
}
這里主要是加載json文件,然后給查詢控件設置屬性。
然后獲得查詢條件,提交給后端API申請數據即可。
json 文件的格式
比較長,發個圖片示意一下:
更多代碼歡迎查看源碼。
源碼
https://gitee.com/naturefw/nf-vite2-element