这是我参与更文挑战的第1天,活动详情查看: 更文挑战
前情回顾
基于 el-form 封装一个依赖 json 动态渲染的表单控件Vue3 封装第三方组件(一)做一个合格的传声筒
功能
使用 vue3 + element-plus 封装了一个查询控件,专为管理后台量身打造,支持各种查询需求:
- 多种查询方式
- 快捷查询
- 更多查询
- 自定义查询
- 支持防抖
- 清空每个查询条件
- 依赖 json 动态创建
有些控件自带清空功能,有些没有自带清空功能,那么就需求我们手动加上清空的功能。
技术栈
- Vite2
- Vue3
- element-plus
查询控件设计
【自我感觉良好的一个脑图】
在线演示
naturefw.gitee.io/nf-vue-cdn/…
进入页面后请点击“查询控件”。
可以在“表单控件”里面添加测试数据,数据会存入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 文件的格式
比较长,发个图片示意一下:
更多代码欢迎查看源码。
源码
在线演示
naturefw.gitee.io/nf-vue-cdn/…