前情回顧
基於 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是一個陣列形式,有多少級就會有多少個陣列。
但是在後端資料庫裡面,往往會分成多個欄位來存放,比如省份用一個欄位表示,城市用一個欄位表示,區縣又是一個欄位表示。
那麼我們在查詢的時候,就需要把查詢結果按照欄位給拆分開,這樣才便於查詢。
所以這裡把查詢結果按照欄位拆分開然後在返回給後端,比如這樣:
{ "a": [ 401, "zhinan" ], "b": [ 401, "shejiyuanze" ], "c": [ 401, "yizhi" ] }
日期
日期查詢比較複雜,這裡對應的資料型別是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