基於 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是一個數組形式,有多少級就會有多少個數組。

但是在后端數據庫里面,往往會分成多個字段來存放,比如省份用一個字段表示,城市用一個字段表示,區縣又是一個字段表示。

那么我們在查詢的時候,就需要把查詢結果按照字段給拆分開,這樣才便於查詢。

所以這里把查詢結果按照字段拆分開然后在返回給后端,比如這樣:

日期

日期查詢比較復雜,這里對應的數據類型是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 文件的格式

比較長,發個圖片示意一下:

查詢控件需要的 json 數據

更多代碼歡迎查看源碼。

源碼

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

在線演示

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM