zoukankan      html  css  js  c++  java
  • 基于 element-plus 封装一个依赖 json 动态渲染的查询控件

    前情回顾

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

    功能

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

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

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

    技术栈

    • Vite2
    • Vue3
    • element-plus

    查询控件设计

    【自我感觉良好的一个脑图】
    查询控件设计.png

    在线演示

    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;65px;text-align:center;">
        <find-kind
          v-model="findChoiceKind"
          :findKind="findKind"
          @change="myChange"
        />
      </div>
      <!--查询内容-->
      <div :style="{ (150 * colCount - 10 ) + 'px'}" style="float:left;">
        <div style="float:left;" :style="{ (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-400px;">
            <el-form
              inline
              label-position="right"
              :model="findItemModel"
              ref="formControl"
              class="demo-form-expand"
              label-width="1px"
              size="mini"
            >
              <el-form-item style="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="{ ( 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="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 文件的格式

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

    查询控件需要的 json 数据

    更多代码欢迎查看源码。

    源码

    https://gitee.com/naturefw/nf-vite2-element

    在线演示

    https://naturefw.gitee.io/nf-vue-cdn/elecontrol/

  • 相关阅读:
    Brain network involved in autonomic functions 与自主功能相关的大脑网络
    Brief summary of classical components of ERP 事件相关成分(ERP)经典成分小结
    ICA & Percentage Variance Account For (PVAF)
    数据处理中白化Whitening的作用图解分析
    Loadings vs eigenvectors in PCA 主成分分析(PCA)中的负荷和特征向量
    主成分分析(PCA)和独立成分分析(ICA)相关资料
    Sketch of heart and QRS complex 心脏及QRS波群简图
    Brain Network visulation in EEG 脑电网络可视化
    Phase Locking Value (PLV) 神经信号的锁相值
    ubuntu16.04下的一些基本操作笔记
  • 原文地址:https://www.cnblogs.com/jyk/p/14841929.html
Copyright © 2011-2022 走看看