Lei Zhang

时光已逝永不回,
往事只能回味。
... ...
春风又吹红了花蕊,
你已经也添了新岁。

▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 96%



iView Cook Book - 多条件、分页、数据展示

2018-12-01 » 没填完的坑 / DataTable , Tutorial , Vuejs

本文基于 iView 2.0, 官网地址: http://v2.iviewui.com/docs/guide/install

本文将着重介绍自己在类 CMS 系统的开发中,如何写一个 "多条件" + "分页" + "数据展示" 的 "富查询" 视图组件思路.

常见场景

如下图所示, 一个 CMS 系统中, 非常常见的场景, 表单筛选 + 分页 + 数据展示, 那么我们如何玩转这个 "富查询" 的逻辑呢?


使用 Form、Table、Page 这三个组件去完成布局 

<template>
  <div>
    <Form inline>
      <!--
        // ...
        // 此处有一些 输入框, 下拉框 等等
      -->
      <FormItem>
        <Button @click="search">查询</Button>
      </FormItem>
    </Form>
    <Table
      :loading="table.loading"
      :data="table.data"
      :columns="table.cols">
    </Table>
    <Page
      :current.sync="pagination.current"
      :total="pagination.total"
      :page-size="pagination.pageSize"
      @on-change="pageChange">
    </Page>
  </div>
</template>

定义 form、table、pagination 三个模型, 监听查询按钮点击, 当前页变化, 当然还需要一个调用接口查询数据的方法

<script>
export default {
  data() {
    return {
      form: {
        name: "",
        status: 0,
        category: "",
        beginDate: "",
        endDate: "",
        // 此处还有一些其他的 form attribute
      },
      table: {
        loading: false,
        data: [],
        cols: [
          // 此处有一些 columns obj ...
        ]
      },
      pagination: {
        current: 1,
        total: 0,
        pageSize: 10,
      }
    }
  },
  created() {
    this.loadData();
  },
  methods: {
    // 调用接口, 查询数据
    loadData() {},
    // 查询按钮点击事件
    search() {},
    // 当前页码变化事件
    pageChange() {}
  }
}
</script>

大致思路

对于 API 来讲, 表单中的筛选条件、当前页两者组合在一起, 才是一个完整的"筛选条件".

在实际的查询交互中, 搜索按钮点击, 当前页应该置为第一页, 不论它当前在哪一页; 每次页码变化, 我们都需要带着表单模型与页码一起调用接口传递参数, 既然每次查询都需要表单模型, 那么我们只需要在 loadData() 中获取即可, search() 与  pageChange() 的区别, 即需不需要将当前页置为第一页.

那么我们该如何去编写 loadData()、search()、pageChange() 这三个函数呢?


search()

按照上述思路, 查询按钮点击时, 将当前页码至为第一页后, 调用查询 API : 

    search() {
      this.pagination.current = 1;
      this.loadData();
    },

pageChange() 

接上述思路, 点击后, 我们不需要修改当前页码, 正常调用查询 API 即可: 

    pageChange() {
      this.loadData();
    }

loadData()

到了最重要的 loadData() 了, 此函数, 首先要在 created 生命周期中被调用一次, 而后每次点击查询按钮或者当前页码变化时, 也会被调用. 因此整个函数需要做这么几件事情: 整合表单模型, 分页模型组成查询条件、调用 API 获取数据、获取数据后初始化表格和分页模型.

    loadData() {
      this.table.loading = true;
      // 整合表单模型,分页模型组成查询条件
      // 假设服务端需要的分页参数对应的 key 为
      // "每页条数" pageSize
      // "当前页码" : pageNum
      const params = Object.assign(
        {},
        this.form,
        {
          pageSize: this.pagination.pageSize,
          pageNum: this.pagination.current
        }
      );
      // 调用 API 获取数据
      axios.post("/api", params)
          .then(response => {
            // 初始化表格和分页模型
            this.table.data = response.data;
            this.pagination.total = response.total;
            this.$nextTick(() => { this.table.loading = false; });
          })
          .catch(error => {
            this.table.loading = false;
          });
    },

真的需要 search()、pageSize() 吗?

发现了吗, search() 与 pageSize() 无非就是将当前页码置为第一页的区别, 那么我们将这一部提取到 loadData() 中, 那么整个 "富查询" 的 methods 便只需要一个函数即可 hold 住全场!


迭代 loadData()

接上述, 我们需要一个标识, 告知 loadData 是否需要将当前页码置为第一页, 其他代码都不需要变, 我们只需要一个参数即可搞定:

  methods: {
    loadData(reload = false) {
      reload && (this.pagination.current = 1);
      // ...
      // 下面无需改动
      // 整个 methods 只需要这一个函数
      // 移除掉 search() & pageSize() 吧
    }
  }

template

<template>
  <div>
    <Form inline>
      <FormItem>
        <!-- loadData(true) -->
        <Button @click="loadData(true)">查询</Button>
      </FormItem>
    </Form>
    <Table :loading="table.loading" :data="table.data" :columns="table.columns"></Table>
    <!-- loadData(false) -->
    <Page
      :current.sync="pagination.current"
      :total="pagination.total"
      :page-size="pagination.pageSize"
      @on-change="loadData"
    ></Page>
  </div>
</template>
 
展开选填信息