zoukankan      html  css  js  c++  java
  • 文档驱动 —— 查询组件:使用 vue3.0 的新特性,重构代码

    源码

    https://github.com/naturefwvue/nf-vue3-ant

    目的

    • 简单方便,不用写代码就可以实现各种查询功能
    • 把查询相关的功能都做全,不留遗憾

    功能

    • 快捷查询,常用的查询条件
    • 个性化查询方案,自己设置自己喜欢的查询条件
    • 可以更换各种查询方式,模糊、精确、范围,随你喜欢
    • 更多的查询条件,查询条件不够用?那就都加上
    • meta驱动,无需代码,不写代码,梦想成真

    结构

    快捷查询


    如上图,把常用的几个查询条件放在第一行,采用紧凑模式,直接放控件,通过 placeholder 的方式标示控件是哪个字段的,这样在有限的空间里面可以多放一两个字段。
    下面可以放功能按钮(添加等)和数据列表,想要查询直接点就行,不用点个按钮,在打开个某某,麻烦。

    个性化查询方案

    每个人都可以有自己的查询习惯,我喜欢(或者工作需要)用这几个查询条件,你喜欢那几个查询条件,快捷查询里就那么几个位置,到底放哪几个字段?
    不用挣,我们可以按照自己的需要设置不同的查询方案,放在快捷里面,你喜欢就行,不影响别人

    更换各种查询方式

    想要用订单编号查询,使用模糊查询还是精确查询?
    精确查询需要把订单号都输入进去,麻烦。
    模糊查询,有可能出现不需要的数据。

    以前做项目,遇到订单号规则升级。老编号五位,新编号十位。用户想查老订单,把订单号都输入全了,结构还是查到一堆不想要的订单,因为是模糊查询。

    现在好了,用户可以自己选择是模糊查询还是精确查询。

    数字类型可以选择等于还是区间查询,甚至大于、小于这些查询方式都可以加上。

    日期类的查询,也可以选择是范围查询,还是查询某一天。

    这样我们做设计的时候就不用纠结,这个字段到底怎么查才适合,把可能的查询方式都给客户,客户自己选好了。

    更多的查询条件

    有些模块,里面的字段非常多,再怎么个性化设置也不够用,那么就需要把全部可以查询的字段都拿出来显示,于是就有了这个全部查询

    多行多列,一个查询条件可以占多个td

    采用<table>的格式的格式,多行多列显示,这样更规则一些,更容易对齐。
    如果有些控件比较长,比如时间的范围查询、多选组等,那么可以设置这些长控件多占用几个td,在调整一下先后顺序,整个页面就可以比较好看,不会出现挤的挤死饿的饿死的情况。
    这里“公司名称”和“公司邮编”占用两列(四个td),下面的日期查询也占用了两列(四个td),这样整体结构比较紧凑,不会有浪费空间的感觉。

    meta 驱动

    字段(控件)需要的属性都放在meta里面,做成单独的json文件,用的时候加载进来就好,所以可以说——实现查询,再也不用写代码了。

    设计思路

    根据查询的特点,封装下面几个控件,顺便把查询方式归纳终结一下。再构思一下查询数据如何存放的问题。

    封装基础控件

    基础控件要比表单简单一些,只需要文本、数字、日期、下拉选择、单选组、多选组这几个。其实单选组也可以变成下拉选择的方式,只是想想有时候做成几个圆圈圈的形式,选择起来更方便一些。

    查询方式

    主要就是等于、不等于、包含、范围区间这几种,只是不同的数据类型会有不同的拼接(查询条件)方式,所以依据不同的数据类型就变成了这么多。
    应该没有漏掉的了。

    查询控件本身的属性

    查询控件要设置显示几列,四列、五列、六列都行,看用户显示器有多宽了。
    要设置快捷查询用哪些查询字段,还有用户自己设置的个性化查询方案。
    这些用于生成table
    后面的就是每个控件需要的meta数据了。

    代码

    查询子控件,比如文本类

    <template>
      <div style="160px;" class="components-input-demo-presuffix">
        <a-input
          :id="'id' + meta.controlId"
          :name="'c' + meta.controlId"
          :value="value"
          :placeholder="meta.placeholder"
          :title="meta.title"
          :maxlength="meta.maxlength"
          :autocomplete="meta.autocomplete"
          :key="'ckey_'+meta.controlId"
          size="small"
          @input="myInput"
          >
            <template v-slot:addonBefore>
              <a-dropdown>
                <a class="ant-dropdown-link">{{kind}}</a>
                <template v-slot:overlay>
                  <a-menu @click="handleMenuClick">
                    <a-menu-item key="401">=</a-menu-item>
                    <a-menu-item key="402">≠</a-menu-item>
                    <a-menu-item key="403">含</a-menu-item>
                  </a-menu>
                </template>
              </a-dropdown>
            </template>
        </a-input>
      </div>
    </template>
    
    <script>
    
    export default {
      name: 'nf-find-input',
      model: {
        prop: 'modelValue',
        event: 'input'
      },
      props: {
        modelValue: String,
        meta: {
          type: Object,
          default: () => {
            return {
              controlId: Number, // 编号,区别同一个表单里的其他控件
              colName: String, // 字段名称
              controlType: Number, // 用类型编号表示type
              placeholder: String,
              title: String, // 提示信息
              maxlength: Number, // 最大字符数
              autocomplete: { // off
                type: String,
                default: 'on'
              }
            }
          }
        }
      },
      data () {
        return {
          value: '',
          kind: '含',
          kindkey: '403',
          findKind: {
            401: '=', // 字符串
            402: '≠',
            403: '含',
            404: '不含',
            405: '起始',
            406: '结束',
            433: '在'
          }
        }
      },
      methods: {
        myInput: function (e) {
          this.value = e.target.value
          this.send()
        },
        handleButtonClick (e) {
          console.log('click left button', e)
        },
        handleMenuClick (e) {
          this.kindkey = e.key
          this.kind = this.findKind[e.key]
          console.log('click', e)
          this.send()
        },
        send: function () {
          var returnValue = []
          returnValue.push(this.kindkey)
          returnValue.push(this.value)
          var colName = this.meta.colName
          this.$emit('update:modelValue', returnValue) // 返回给调用者
          this.$emit('getvalue', returnValue, colName) // 返回给中间组件
        }
      }
    }
    </script>
    

    这个比表单用的子控件要简单很多。
    其他的就不贴了,大同小异。

    查询控件

    暂时没有做成单独的控件,之前是没重构的代码,现在用vue3.0的新方法重构了一下。

    <template>
      <div class="home">
        <h1>查询演示</h1>
        <div style="background-color:#dddddd;height:600px;100px;float:left;">
          <a href="#" @click="myClick('companyFind')">公司信息</a> <br>
          <a href="#" @click="myClick('personFind')">员工信息</a>
        </div>
        <div style="background-color:#eee;height:600px;1100px;float:left;">
          <!--快捷查询,一行-->
          <div
            :style="{
              height: '70px',
              overflow: 'hidden',
              position: 'relative',
              border: '1px solid #ebedf0',
              borderRadius: '2px',
              padding: '2px',
              textAlign: 'left',
              background: '#fafafa',
            }"
          >
            <div class="ant-table ant-table-body ant-table-default ant-table-bordered" >
              <table role="all">
                <tbody class="ant-table-tbody">
                  <tr>
                    <td><!--个性化查询方案-->
                      <a-dropdown size="small">
                        <template v-slot:overlay>
                          <a-menu @click="changeQuickFind">
                            <a-menu-item v-for="(item,key) in findMeta.customer" :key="key">
                              <UserOutlined />{{item.name}}
                            </a-menu-item>
                          </a-menu>
                        </template>
                        <a-button style="margin-left: 8px"> 快捷 <DownOutlined /> </a-button>
                      </a-dropdown>
                    </td>
                    <template v-for="key in quickFindKey" :key="key">
                      <td align="left" style="padding:3px 3px;height:20px">
                        <nfInput v-model="modelValue[getMeta(key).colName]"
                        :meta="findItem[key]" />
                      </td>
                    </template>
                    <td><a-button type="primary" @click="moreFindShow(true)">更多</a-button></td>
                  </tr>
                </tbody>
              </table>
            </div>
          </div>
          <!--更多查询条件,用抽屉打开-->
          <a-drawer
            title="更多查询条件"
            placement="top"
            :closable="false"
            :visible="findVisible"
            @close="moreFindShow(false)"
          >
            <div class="ant-table ant-table-body ant-table-default ant-table-bordered" >
              <table role="all">
                <tbody class="ant-table-tbody">
                  <template v-for="(tr, index) in findTable" :key="index"><!--循环行-tr-->
                    <tr>
                      <template v-for="(td, index2) in tr" :key="index+'-'+index2"><!--循环列-td-->
                        <td align="right" style="padding:3px 3px;height:20px">
                          {{findItem[td].title}}:
                        </td>
                        <td :colspan="findItem[td].tdCount" align="left" style="padding:3px 3px;height:20px">
                          <nfInput v-model="modelValue[getMeta(td).colName]"
                          :meta="findItem[td]" />
                        </td>
                      </template>
                    </tr><!--循环行-tr 结束 -->
                  </template>
                </tbody>
              </table>
            </div>
          </a-drawer>
        </div>
      </div>
    </template>
    

    发现个问题 v-model="modelValue[getMeta(td).colName]" 这里的中括号不让嵌套,所以只好写个函数了。
    这里就是两套table,一个是单行的,用于快捷查询,一个多多行多列的,用于显示全部查询条件。

    meta格式

    {
      "companyFind":{
        "findMeta":{
          "quickFind":[1009,1004,1005,1002],
          "allFind":[1000,1001,1002,1003,1009,1004,1005,1010,1006,1007,1011,1008],
          "colCount":4,
          "customer":{
            "100": {
              "name":"方案一1",
              "keys":[1009,1004,1005]
            },
            "101":{
              "name":"方案二2",
              "keys":[1006,1004,1005]
            }
          }
        },
        "findItem":{
          "1000":{
              "controlId": 1000,
              "colName": "companyName",
              "controlType": 101,
              "class": "",
              "placeholder": "公司名称",
              "title": "公司名称",
              "size": 10,
              "maxlength": 100,
              "optionList": [],
              "findKind":"402",
              "tdCount":1
          },
          "1001":{
              "controlId": 1001,
              "colName": "companyCode",
              "controlType": 131,
              "class": "",
              "placeholder": "公司邮编",
              "title": "公司邮编",
              "min": 100000,
              "max": 999999,
              "step": 1,
              "maxlength": 6,
              "optionList": [] ,
              "findKind":"401",
              "tdCount":1
          }
          ...... 更多查询条件
        }
      }
    }
    

    有两个主要部分,一个是对查询本身进行描述,一个是对查询条件的控件进行描述。

    js代码

    首先定义一个管理函数(管理类)

    // 查询组件管理
    const findManage = () => {
      // 渲染查询条件,内容不会变,就不监控了
      const findWhere = {
        401: ' = "{k}"',
        402: ' <> "{k}"',
        403: ' like "%{k}%"',
        404: ' not like "%{k}%"',
        405: ' like "{k}%"',
        406: ' like "%{k}"',
        411: ' ={k}',
        412: ' <>{k}',
        413: ' >{k}',
        414: ' >={k}',
        415: ' <{k}',
        416: ' <={k}',
        421: ' ="{k}"',
        431: ' between {k1} and {k2}',
        432: ' between "{k1}" and "{k2}" ',
        433: ' in ({k})'
      }
      // 加载meta信息,json格式
      const json = require('./FindDemo.json')
      const findMeta = ref({}) // 查询表单的meta信息
      const findItem = ref({}) // 表单需要的meta信息
      const modelValue = ref({}) // 放数据的model
      const quickFindKey = ref({}) // 存放快捷查询的key
      const findTable = ref([]) // 二维数组存放meta的ID
      // 把查询里的字段,变成多行多列的分布,二维数组
      const metaToTable = () => {
        var tdCount = 0
        var td = []
        findTable.value = []
        for (var index in findMeta.value.allFind) { // 遍历设定的meta的key的数组
          var key = findMeta.value.allFind[index]
          var meta = findItem.value[key]
          td.push(key)
          tdCount += 1 + meta.tdCount
          if (tdCount >= findMeta.value.colCount * 2) {
            findTable.value.push(td)
            td = []
            tdCount = 0
          }
        }
        if (td.length > 0) {
          findTable.value.push(td)
        }
      }
      // 更换个性化查询方案
      const changeQuickFind = (e) => {
        quickFindKey.value = findMeta.value.customer[e.key].keys
      }
      // 切换其他查询模块
      const myClick = (key) => {
        // 更换表单的meta
        findMeta.value = json[key].findMeta
        findItem.value = json[key].findItem
        // 加载快捷查询
        quickFindKey.value = json[key].findMeta.quickFind
        // 初始化
        modelValue.value = {}
        // 创建model
        modelValue.value = {}
        for (var k in findItem.value) {
          var item = findItem.value[k]
          modelValue.value[item.colName] = ''
        }
    
        metaToTable()
      }
    
      // 抽屉的事件
      const findVisible = ref(false)
      const moreFindShow = (isShow) => {
        findVisible.value = isShow
      }
      return {
        modelValue,
        findMeta,
        findItem,
        quickFindKey,
        findWhere,
        findTable,
        myClick,
        findVisible,
        moreFindShow
      }
    }
    

    首先定义需要的数据,然后定义事件,最后通过return 的方式暴露出来共用的数据和方法。

    然后在setup里面引用就可以了,这样相同功能的代码可以写在一起,setup只是一个统一调度的地方,代码可以清洗很多。

    setup () {
        // 引入查询管理
        const { modelValue, findMeta, findItem, findWhere, quickFindKey, myClick, changeQuickFind, findTable, findVisible, moreFindShow } = findManage()
        myClick('companyFind')
        // 通过key获取meta
        const getMeta = (td) => {
          return findItem.value[td]
        }
        return {
          findVisible,
          moreFindShow,
          changeQuickFind,
          modelValue,
          findItem,
          findMeta,
          quickFindKey,
          findWhere,
          findTable,
          myClick,
          getMeta
        }
      }
    

    一般一个“页面”里不会只有一个查询,至少也得来个数据列表+分页才行,同理数据列表的相关代码也可以单独写个管理类,这样setup里面只是调用这些类,可读性会显著提高。

    感想

    Ant Design Vue,研究了几天,感觉有个强大的UI库,太方便了。其实以前就一直想做这种方式的查询控件,但是css很烂,一些效果做不出来,比如抽屉形式的更多查询条件、查询方式的切换、个性化方案的选择等。
    日期控件太复杂了,研究了好几天还是没用研究透,还需要继续专研,因为关于日期时间查询的地方还有一些细节没有实现好。

    one more thing

    后面就是数据列表、分页。然后一个模块的增删改查就全了。
    又研究了一下ant design vue pro。好多东西都不用自己再写了,轻松很多,然后就是把自己的想法注入进去了。

  • 相关阅读:
    [LeetCode] 21. 合并两个有序链表
    [LeetCode] 5081. 步进数
    [LeetCode] 104. 二叉树的最大深度
    [LeetCode] 70. 爬楼梯
    Java开发手册1.5读书笔记
    [LeetCode] 509. 斐波那契数
    设计模式之UML类图以及类间关系
    [LeetCode] 50. Pow(x, n)
    [LeetCode] 206. 反转链表
    [LeetCode] 119. 杨辉三角 II
  • 原文地址:https://www.cnblogs.com/jyk/p/13696737.html
Copyright © 2011-2022 走看看