基于 element-plus 封装一个依赖 json 动态渲染的查询控件

简介: 封装一个依赖 json 动态渲染的查询控件

image.png


这是我参与更文挑战的第1天,活动详情查看: 更文挑战


前情回顾



基于 el-form 封装一个依赖 json 动态渲染的表单控件Vue3 封装第三方组件(一)做一个合格的传声筒


功能



使用 vue3 + element-plus 封装了一个查询控件,专为管理后台量身打造,支持各种查询需求:


  • 多种查询方式
  • 快捷查询
  • 更多查询
  • 自定义查询
  • 支持防抖
  • 清空每个查询条件
  • 依赖 json 动态创建


有些控件自带清空功能,有些没有自带清空功能,那么就需求我们手动加上清空的功能。


技术栈



  • Vite2
  • Vue3
  • element-plus


查询控件设计



【自我感觉良好的一个脑图】


image.png


在线演示



naturefw.gitee.io/nf-vue-cdn/…


进入页面后请点击“查询控件”。


可以在“表单控件”里面添加测试数据,数据会存入webSQL。 受限于webSQL,有些查询功能无法实现。


功能演示



查询功能具体是什么样子呢?我们先来看一段视频:


点击查看视频演示


各种查询方式



查询控件针对不同的数据类型(后端数据库字段类型),量身打造了多种查询方式,让查询更便捷!


文本


image.png


针对文本类的数据类型(varchar、text等),提供常用的模糊查询(包含)、精确查询(=),还有起始于、结束于等查询方式可供选择。 这样用户可以更灵活方便的进行查询操作。


数字


image.png


针对数值类型(int、float、decme等),提供常用的精确查询(=)、范围查询(从xx到xxx)还有大于等于等查询方式。


单选组的查询


image.png


image.png


针对枚举类型,int 或者 varchar 等有限数量。


单选组有两种情况,一个是常见的查询一种情况即可,选择第一选项那么只需要显示第一个选项对应的数据。 另一个就是想同时看多个选项的结果,那么这时候还用单选组的方式就不适合了,需要变成多选组的方式,这样才可以让用户选择多个选项。


所以这里的单选的查询支持两种查询方式:


  • =: 只能查询一个选项,对应单选。


  • 包含:可以同时查询多个选项,对应多选。


支持清空查询条件,即点击右侧的“x”。


多选支持防抖。


勾选和开关


image.png


二者对应的数据类型是 bool 型的(bit),所以只有“=”这一种查询方式,增加了一个“清空”的按钮,这样可以单独清掉查询条件。


级联选择


image.png


见的级联选择是省市区县的选择,组件默认给的model是一个数组形式,有多少级就会有多少个数组。


但是在后端数据库里面,往往会分成多个字段来存放,比如省份用一个字段表示,城市用一个字段表示,区县又是一个字段表示。


那么我们在查询的时候,就需要把查询结果按照字段给拆分开,这样才便于查询。


所以这里把查询结果按照字段拆分开然后在返回给后端,比如这样: { "a": [ 401, "zhinan" ], "b": [ 401, "shejiyuanze" ], "c": [ 401, "yizhi" ] }


日期


日期查询比较复杂,这里对应的数据类型是date,选择后返回的数据是“2021-05-20”的形式。 然后就是如何让用户感觉爽的问题了。


  • 常规查询方式


image.png


一般都是如上图所示,直接选择日期范围,这个看起来似乎没有啥问题,可以选择任意日期。


但是如果用户想查询2021年1月到2021年3月的数据,那么用户的操作就会比较繁琐。 我们来看看一共要点击几次鼠标? 打开日期栏 》 找到一月份(n次) 》 选择一号 》 找到三月份(又是n次) 》选择31号。 整个流程需要点好多次鼠标,实在是太麻烦了。


  • 通过月份查询日期范围


如果可以直接选择月份呢?像这样:


image.png


如果用户想选择多个月份的日期,可以通过“从” + “年月”的形式,选择起始月份即可,返回的数据是"2021-01-01", "2021-03-31" 的形式。


image.png


如果客户想选择一个月的范围,那么可以用“=” + “年月”的方式来选择(如上图),返回的数据是"2021-02-01", "2021-02-28" 的形式。


这样用户就非常方便了,节省了n次鼠标点击。不过这还没有结束,还有选择“年”的情况。


  • 通过年查询日期范围


如果要查询一年的或者多年的日期范围呢?我们可以选择“年”的方式。


image.png


如果选择一整年的话,我们可以使用“=” + “年”的方式(如上图),选择需要的年份即可,返回的数据是 "2021-01-01", "2021-12-31"  的形式。


image.png


如果选择连续的多个年份,可以用“从” + “年”的方式(如上图),选择起始年份即可,返回的数据是"2021-01-01", "2022-12-31" 的形式。


年、年月、年周的查询


上面是针对date类型的数据,这里是针对int、varchar类型的数据。 有时候为了加快查询速度,数据库设计上面可能会用增加“冗余字段”的方式来提升性能,比如增加“年”的字段,类型是int,存放“2021”、“2022”这样的数据。 同理,可以增加“年月”的字段,类型是int,存放“202101”、“202103”这类的数据,还有“年周”的情况。

这里的查询方式就是针对这种情况来设计的。


  • 年的查询


image.png


image.png


要比日期查询简单很多。


  • 年月的查询


image.png


image.png


  • 年周的查询


这里不是指星期几,而是一年内的第几周,听说有些企业是按照周来安排工作的,所以这里也提供了周的查询。


image.png

image.png


日期时间的查询


image.png


快速查询


显示常用的查询条件。


image.png


自定义查询方案


可以把常用的查询字段放在一起,组成一个查询方案,方便用户使用。


image.png


更多查询


显示全部查询条件,查询后的字段可以带入快捷查询,便于随时更改查询条件。


image.png


image.png


文件结构



上面都是介绍功能,下面开始介绍一下实现方式。 首先看一下文件结构:


image.png


  • 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 文件的格式



比较长,发个图片示意一下:


image.png


更多代码欢迎查看源码。


源码



gitee.com/naturefw/nf…


在线演示



naturefw.gitee.io/nf-vue-cdn/…


相关文章
|
7月前
|
存储 JSON Apache
揭秘 Variant 数据类型:灵活应对半结构化数据,JSON查询提速超 8 倍,存储空间节省 65%
在最新发布的阿里云数据库 SelectDB 的内核 Apache Doris 2.1 新版本中,我们引入了全新的数据类型 Variant,对半结构化数据分析能力进行了全面增强。无需提前在表结构中定义具体的列,彻底改变了 Doris 过去基于 String、JSONB 等行存类型的存储和查询方式。
揭秘 Variant 数据类型:灵活应对半结构化数据,JSON查询提速超 8 倍,存储空间节省 65%
|
JSON JavaScript 数据格式
vue-element-admin表格json数据渲染,异常数据一行显示红色
vue-element-admin表格json数据渲染,异常数据一行显示红色
120 1
|
1月前
|
安全 JavaScript
如何在`package.json`中正确设置依赖版本范围?
正确设置 `package.json` 中的依赖版本范围需要综合考虑项目的需求、依赖库的稳定性和兼容性,以及开发和维护的便利性等因素。通过合理选择版本范围符号,并结合定期的审查和测试,可以有效地管理项目依赖,确保项目的稳定运行。
41 1
|
2月前
|
JSON JavaScript API
(API接口系列)商品详情数据封装接口json数据格式分析
在成长的路上,我们都是同行者。这篇关于商品详情API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦!
|
7月前
|
JSON Java 数据安全/隐私保护
java中的http请求的封装(GET、POST、form表单、JSON形式、SIGN加密形式)
java中的http请求的封装(GET、POST、form表单、JSON形式、SIGN加密形式)
566 1
|
5月前
|
JSON Java fastjson
Spring Boot返回Json数据及数据封装
本文详细介绍了如何在Spring Boot项目中处理JSON数据的传输 Spring Boot默认使用Jackson作为JSON处理器,并通过`spring-boot-starter-web`依赖自动包含相关组件。文章还展示了如何配置Jackson处理null值,使其转换为空字符串。此外,文章比较了Jackson和FastJson的特点,并提供了FastJson的配置示例,展示了如何处理null值以适应不同应用场景。
|
5月前
|
JSON 数据格式
OkHttp json rpc 查询ETH余额
OkHttp json rpc 查询ETH余额
|
6月前
|
JSON JavaScript 数据格式
1.js动态的往json数据添加新属性和值 2.JSON 和 JS 对象互转 3.对象转化为数组
1.js动态的往json数据添加新属性和值 2.JSON 和 JS 对象互转 3.对象转化为数组
57 0
|
7月前
|
JSON 数据格式 索引
python之JMESPath:JSON 查询语法库示例详解
python之JMESPath:JSON 查询语法库示例详解
81 0
|
JSON JavaScript 前端开发
ztree+json,渲染树形菜单
ztree+json,渲染树形菜单
81 1
下一篇
DataWorks