前端开发设计-常用数据查询设计

1,795 阅读3分钟

前端开发设计-常用数据查询设计

本文纯个人刚开始尝试开发总结,写的不好,大家随便看看。另外我这还有个前端小群(9个人),基本纯问题回答,有想加的可以微我:youqian_clw。发广告的就别来了,谢谢呀。

常用的数据查询一般由3个组件组成,一个是生成查询条件的form组件,一个是生成表格的table组件,一个是生成分页的pagination组件。

常见的数据查询如此,有时候还会左侧有组织树,点击树节点进行数据查询。但这个必要性并不高,或者说关联性不高,我们要设计的是 常用的数据查询封装组件,而非高定制性的数据查询组件。

一、查询条件设计

查询条件,一般如下图:

Snipaste_2022-02-16_15-47-22.png

这是我用element的form组件设计,下面是直接用element官网form组件生成的:

Snipaste_2022-02-16_15-52-58.png

很明显,排版并不好看,所以我们需要先进行样式优化。

另外考虑查询条件需要支持多种多样,比如文本输入,单选,多选,远程搜索,日期,范围日期等等。

首先先生成组件

因为使用的是element,所以直接复制官网的form组件,然后把可能的查询条件格式一股脑扔进去,当然,都是常用的,特殊比较困难的可以自己单独设计。然后就生成了这样的效果: Snipaste_2022-02-16_15-52-58.png

然后处理样式

然后我们用grid布局进行排版,form组件上设置样式:

display: grid;
grid-template-columns: repeat(4, 25%);

效果如下,看起来有点顺序了: Snipaste_2022-02-16_16-01-12.png

然后对输入框进行处理,因为element支持左侧文本这块长度的设定,那么只要处理好输入框这块就行了,让它100%占据除文本外的内容即可。

很快效果就完成了:

Snipaste_2022-02-16_15-47-22.png

接着处理dom

样式完成,我们需要对dom进行整理。抽离共用部分,查询条件数据类型设置为数组,遍历数组进行查询条件渲染。数组的每个元素都是一个对象,里面配置了这个查询条件的各种所需,包括key,条件名,条件类型,如果类型是下拉框,加个list参数配置下拉选项。

image-20220216161141982.png

上面是整理前,下面是整理后:

image-20220216161439717.png

看起来舒服多了。

二、表格+分页设计

我个人喜欢把两者设计在一个组件中,如果我不需要分页,我加个v-if判断即可。

这个组件设计主要是布局排版,其他没什么难度。对了,table的高度要高度自适应,所以需要在页面初始化时以及监听resize,设置table的高度,逻辑就是:

this.tableHeight = this.$refs.tablePagination.clientHeight - this.$refs.pagination?.clientHeight || 0;

超级简单,把大容器高度算出来,减掉非table组件的高度就是table的高度了。

我觉得还是直接贴代码大家自己在项目里试试,对,我不想写了,以后再重新编辑

前缀:vue-cli搭建,elementui引入。

查询条件组件:

<!--
 condition.vue
 
 查询条件格式类型:
 - 输入框:
 - 下拉框:单选下拉框,多选下拉框,固定下拉框,搜索下拉框,远程搜索下拉框
 - 日期框:单时间,范围时间,单日期,范围日期

 formList具体格式:
  key: form绑定的key值。
  defaultValue: 默认值。
  type: formItem类型,有text-输入框文本,select-单选下拉框,mSelect-多选下拉框,rSelect-远程搜索下拉框,time-单时间,
        rTime-范围时间,date-单日期,rDate-范围日期,radio-单选框。
  list: 下拉选项。
  label: label名。
  placeholder: 提示信息。
  isShow: 是否显示。
 -->
<template>
  <div class="condition">
    <el-form ref="form" :inline="true" :model="form" class="form" :style="{gridTemplateColumns: `repeat(${cols}, ${100/cols}%)`}" :size="size" :label-width="labelWidth">
      <template v-for="(item, index) in formList">
        <el-form-item :label="item.label" :prop="item.key">
          <!-- 文本输入框 -->
          <el-input v-if="item.type === 'text'" v-show="item.isShow" class="width100" v-model="form[item.key]" :placeholder="item.placeholder"></el-input>
          <!-- 单选下拉框 -->
          <el-select v-else-if="item.type === 'select'" v-show="item.isShow" class="width100"  v-model="form[item.key]" :placeholder="item.placeholder" clearable>
            <el-option v-for="li in item.list" :label="li.label" :value="li.value"></el-option>
          </el-select>
          <!-- 多选下拉框 -->
          <el-select v-else-if="item.type === 'mSelect'" v-show="item.isShow" class="width100"  v-model="form[item.key]" :placeholder="item.placeholder" multiple collapse-tags>
            <el-option v-for="li in item.list" :label="li.label" :value="li.value"></el-option>
          </el-select>
          <!-- 单时间 -->
          <el-time-select v-else-if="item.type === 'time'" v-show="item.isShow" class="width100" v-model="form[item.key]" :placeholder="item.placeholder"></el-time-select>
          <!-- 范围时间 -->
          <el-time-picker v-else-if="item.type === 'rTime'" v-show="item.isShow" class="width100" is-range v-model="form[item.key]" range-separator="-" start-placeholder="开始时间" end-placeholder="结束时间" :placeholder="item.placeholder"></el-time-picker>
          <!-- 单日期 -->
          <el-date-picker v-else-if="item.type === 'date'" v-show="item.isShow" class="width100" v-model="form[item.key]" type="date" :placeholder="item.placeholder"></el-date-picker>
          <!-- 范围日期 -->
          <el-date-picker v-else-if="item.type === 'rDate'" v-show="item.isShow" class="width100" v-model="form[item.key]" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :placeholder="item.placeholder"></el-date-picker>
        </el-form-item>
      </template>
      <el-form-item>
        <el-button type="primary" :size="size" @click="handlerSubmit">查询</el-button>
        <el-button type="ghost" :size="size" @click="resetForm">重置</el-button>
        <i class="el-icon-d-arrow-right" :style="{transform: isShowAllCondition ? 'rotate(270deg)' : 'rotate(90deg)'}" @click="isShowAllCondition = !isShowAllCondition"></i>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
  export default {
    name: "condition",
    props: {
      labelWidth: {
        type: String,
        default: '120px'
      },
      cols: {
        type: Number,
        default: 4
      },
      formList: {
        type: Array,
        default: []
      },
      size: {
        type: String,
        default: 'small' // medium / small / mini
      }
    },
    data() {
      return {
        isShowAllCondition: false,
        form: {}
      }
    },
    watch: {
      formList(val) {
        val.forEach(item => {
          // 生成form
          this.form[item.key] = item.defaultValue || ''
        });
      }
    },
    methods: {
      // 查询
      handlerSubmit() {
        this.$emit('query', this.form);
      },
      // 重置
      resetForm() {
        debugger;
        this.$refs.form.resetFields();
      }
    },
    mounted() {
      // js动态设置样式变量
      document.querySelector('body').style.setProperty('--query-form-label-width', this.labelWidth);
    }
  }
</script>

<style>
/* css的变量用法 */
:root{
  --query-form-label-width: 120px;
}
</style>

<style scoped lang="scss">

$query-form-label-width: var(--query-form-label-width);
.form{
  text-align: left;
  display: grid;
  grid-template-columns: repeat(4, 25%);
  /deep/.el-form-item__content{
    width: calc(100% - var(--query-form-label-width));
  }
  .width100{
    width: 100%;
  }
  .el-icon-d-arrow-right{
    cursor: pointer;
    margin-left: 10px;
  }
}
</style>

表格+分页组件:

<!-- tablePagination.vue -->
<template>
  <div class="table-pagination" ref="tablePagination">
    <div class="table">
      <el-table :data="tableData" :size="size" :height="tableHeight" border stripe style="width: 100%">
        <template v-for="(item, index) in columns">
          <el-table-column :prop="item.key" :label="item.label" :width="item.width"></el-table-column>
        </template>
      </el-table>
    </div>
    <div class="pagination" ref="pagination">
      <el-pagination
          @size-change="changePageSize"
          @current-change="changePage"
          :current-page="currentPage"
          :page-sizes="pageSizeOpt"
          :page-size="currentPageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="tableData.length">
      </el-pagination>
    </div>
  </div>
</template>

<script>
  export default {
    name: "tablePagination",
    props: {
      tableData: {
        type: Array,
        default: []
      },
      columns: {
        type: Array,
        default: []
      },
      size: {
        type: String,
        default: 'small'
      }
    },
    data() {
      return {
        currentPageSize: 10,
        currentPage: 1,
        pageSizeOpt: [10, 20, 50, 100],
        tableHeight: 200
      }
    },
    methods: {
      changePage(val) {
        this.currentPage = val;
        this.getTableData();
      },
      changePageSize(val) {
        this.currentPageSize = val;
        this.getTableData();
      },
      getTableData() {
        this.$emit('getTableData', {
          page: this.currentPage,
          pageSize: this.currentPageSize
        })
      },
      initTableHeight() {
        this.tableHeight = this.$refs.tablePagination.clientHeight - this.$refs.pagination?.clientHeight || 0;
      }
    },
    mounted() {
      this.$nextTick(() => {
        this.initTableHeight();
        window.addEventListener('resize', this.initTableHeight);
      });
    },
    activated() {
      window.addEventListener('resize', this.initTableHeight);
    },
    deactivated() {
      window.removeEventListener('resize', this.initTableHeight);
    },
    beforeDestroy() {
      window.removeEventListener('resize', this.initTableHeight);
    }
  }
</script>

<style scoped lang="scss">
.table-pagination{
  flex: 1;
  display: flex;
  flex-direction: column;
  .table{
    flex: 1;
  }
  .pagination{
    text-align: right;
  }
}
</style>

归总,数据查询页面展示:

<!-- 查询组件 -->
<template>
  <div class="query-container">
    <condition :form-list="formList" @query="query"></condition>
    <tablePagination ref="tablePagination" :columns="columns" :table-data="tableData" @getTableData="getTableData"></tablePagination>
  </div>
</template>

<script>
  // 引入查询条件组件和表格+分页组件
  import Condition from '../components/query/condition';
  import TablePagination from '../components/query/tablePagination';
  export default {
    name: "searchComponent",
    components: {
      Condition,
      TablePagination
    },
    data() {
      return {
        formList: [
          {
            key: 'user',
            label: '文本输入框',
            defaultValue: '',
            placeholder: '请输入审批人',
            type: 'text',
            isShow: true
          },
          {
            key: 'select',
            label: '单选下拉框',
            type: 'select',
            isShow: true,
            list: [
              {
                label: '选项1',
                value: 'val1'
              },
              {
                label: '选项2',
                value: 'val2'
              }
            ]
          },
          {
            key: 'mSelect',
            label: '多选下拉框',
            type: 'mSelect',
            isShow: true,
            list: [
              {
                label: '选项1',
                value: 'val1'
              },
              {
                label: '选项2',
                value: 'val2'
              }
            ]
          },
          {
            key: 'time',
            label: '单时间',
            isShow: true,
            type: 'time'
          },
          {
            key: 'rTime',
            label: '范围时间',
            isShow: true,
            type: 'rTime'
          },
          {
            key: 'date',
            label: '单日期',
            isShow: true,
            type: 'date'
          },
          {
            key: 'rDate',
            label: '范围日期',
            isShow: true,
            type: 'rDate'
          },
        ],
        tbData: [
          {
            key1: '1',
            key2: '2',
            key3: '3'
          },
          {
            key1: '11',
            key2: '22',
            key3: '33'
          },
          {
            key1: '111',
            key2: '222',
            key3: '333'
          },
          {
            key1: '1',
            key2: '2',
            key3: '3'
          },
          {
            key1: '11',
            key2: '22',
            key3: '33'
          },
          {
            key1: '111',
            key2: '222',
            key3: '333'
          },
          {
            key1: '1',
            key2: '2',
            key3: '3'
          },
          {
            key1: '11',
            key2: '22',
            key3: '33'
          },
          {
            key1: '111',
            key2: '222',
            key3: '333'
          },
          {
            key1: '1',
            key2: '2',
            key3: '3'
          },
          {
            key1: '11',
            key2: '22',
            key3: '33'
          },
          {
            key1: '111',
            key2: '222',
            key3: '333'
          },
          {
            key1: '1',
            key2: '2',
            key3: '3'
          },
          {
            key1: '11',
            key2: '22',
            key3: '33'
          },
          {
            key1: '111',
            key2: '222',
            key3: '333'
          },
        ],
        tableData: [],
        columns: [
          {
            key: 'key1',
            label: '标题1'
          },
          {
            key: 'key2',
            label: '标题2'
          },
          {
            key: 'key3',
            label: '标题3'
          }
        ]
      }
    },
    methods: {
      query(val) {
        let tablePagination = this.$refs.tablePagination;
        this.getTableData({
          ...val,
          page: tablePagination.currentPage,
          pageSize: tablePagination.currentPageSize
        })
      },
      getTableData(param) {
        this.tableData = this.tbData.slice(param.page-1, param.page * param.pageSize);
      }
    }
  }
</script>

<style scoped>
.query-container{
  height: 100%;
  display: flex;
  flex-direction: column;
}
</style>