使用 Vue.js 创建网格组件

网上有很多关于现代网格用户界面组件应该是什么样子以及它应该提供哪些功能的信息,一个常见的要求是网格组件易于可视化并对信息采取行动。

使用 Vue.js 很容易做到这一点,在本文中,我们将构建一个包含以下用户体验的网格组件:

  • 固定的标题和可滚动的表体
  • 表页之间的页面
  • 能够改变每页的行数

我会假设你已经安装了 Vue.js。

<$>[注意]请注意,下面的代码不是生产准备的,是为教育目的而在这里呈现的。

代码

下面的代码是从 官方 Vue.js 文档中取出的修改示例。

创建一个新的单个文件组件,Grid.vue,使用以下代码:

 1[label Grid.vue]
 2<div class="wrapper">
 3  <form id="search">
 4    Search <input name="query" v-model="searchQuery">
 5  </form>
 6  <div id="grid-template">
 7    <div class="table-header-wrapper">
 8      <table class="table-header">
 9        <thead>
10          <th v-for="key in columns"
11            @click="sortBy(key)"
12            :class="{ active: sortKey == key }"
13          >
14            {{ key | capitalize }}
15            <span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'"></span>
16          </th>
17        </thead>
18      </table>
19    </div>
20    <div class="table-body-wrapper">
21      <table class="table-body">
22        <tbody>
23          <tr v-for="entry in filteredData">
24            <td v-for="key in columns"> {{entry[key]}}</td>
25          </tr>
26        </tbody>
27      </table>
28    </div>
29  </div>
30</div>
 1export default {
 2  name: "grid",
 3  props: {
 4    data: Array,
 5    columns: Array,
 6  },
 7  data(){
 8    return {
 9      searchQuery: '',
10      sortKey: '',
11      sortOrders: {},
12    }
13  },
14  computed: {
15    filteredData: function () {
16      let sortKey = this.sortKey;
17      let filterKey = this.searchQuery && this.searchQuery.toLowerCase();
18      let order = this.sortOrders[sortKey] || 1;
19      let data = this.data;
20
21      if (filterKey) {
22        data = data.filter(function (row) {
23          return Object.keys(row).some(function (key) {
24            return String(row[key]).toLowerCase().indexOf(filterKey) > -1;
25          })
26        })
27      }
28      if (sortKey) {
29        data = data.slice().sort(function (a, b) {
30          a = a[sortKey];
31          b = b[sortKey];
32          return (a === b ? 0 : a > b ? 1 : -1) * order;
33        })
34      }
35    return data;
36    },
37  },
38  filters: {
39    capitalize: function (str) {
40      return str.charAt(0).toUpperCase() + str.slice(1);
41    }
42  },
43  methods: {
44    sortBy: function (key) {
45      this.sortKey = key;
46      this.sortOrders[key] = this.sortOrders[key] * -1
47    },
48  },
49  created(){
50    let sortOrders = {};
51    this.columns.forEach(function (key) {
52      sortOrders[key] = 1;
53    })
54    this.sortOrders = sortOrders;
55  }
56}
 1body{
 2  font-family: Helvetica Neue, Arial, sans-serif;
 3  font-size: 14px;
 4  color: #555;
 5}
 6
 7table {
 8  border-spacing: 0;
 9  width: 100%;
10
11}
12
13th {
14  background-color: #008f68;
15  color: rgba(255,255,255,0.66);
16  cursor: pointer;
17  -webkit-user-select: none;
18  -moz-user-select: none;
19  -ms-user-select: none;
20  user-select: none;
21}
22
23td {
24  border-bottom: 1px #008f68 solid;
25}
26
27th, td {
28  min-width: 150px;
29  padding: 10px 20px;
30}
31
32th.active {
33  color: #fff;
34}
35
36th.active .arrow {
37  opacity: 1;
38}
39
40.arrow {
41  display: inline-block;
42  vertical-align: middle;
43  width: 0;
44  height: 0;
45  margin-left: 5px;
46  opacity: 0.66;
47}
48
49.arrow.asc {
50  border-left: 4px solid transparent;
51  border-right: 4px solid transparent;
52  border-bottom: 4px solid #FAE042;
53}
54
55.arrow.dsc {
56  border-left: 4px solid transparent;
57  border-right: 4px solid transparent;
58  border-top: 4px solid #FAE042;
59}
60
61#grid-template {
62  display: flex;
63  display: -webkit-flex;
64  flex-direction: column;
65  -webkit-flex-direction: column;
66  width: 600px;
67}

现在将该组件导入您的App.vue文件并添加一些数据:

 1[label App.vue]
 2import Grid from './Grid'
 3export default {
 4  name: 'app',
 5    data() {
 6      return {
 7        gridData: [
 8          { name: 'American alligator', location: 'Southeast United States' },
 9          { name: 'Chinese alligator', location: 'Eastern China' },
10          { name: 'Spectacled caiman', location: 'Central & South America' },
11          { name: 'Broad-snouted caiman', location: 'South America' },
12          { name: 'Jacaré caiman', location: 'South America' },
13          { name: 'Black caiman', location: 'South America' },
14        ],
15        gridColumns: ['name', 'location'],        
16      }
17  },
18  components: {
19    Grid
20  }
21}

使用组件通过传输相应的数据:

1<grid :data="gridData" :columns="gridColumns"></grid>

接下来,让我们用我们介绍中提到的额外功能来扩展这个组件。

固定 Header

我正在使用一篇文章中的技术(https://joshondesign.com/2015/05/23/csstable)和标记已经根据本文设置。

 1.wrapper{
 2  height: 300px;
 3}
 4
 5#grid-template > .table-body-wrapper {
 6  width: 100%;
 7  overflow-y: scroll;
 8}
 9
10#grid-template {
11  height: 100%;
12}
13
14#grid-template > .table-body-wrapper {
15  flex: 1;
16}

页面

首先,让我们定义每页的行数,以及起始位置. 为此,将添加两个新的属性:

1startRow: 0,
2rowsPerPage: 10

让我们添加显示当前页面号码的导航标记:

1<div id="page-navigation">
2  <button>Back</button>
3  <p>{{startRow / rowsPerPage + 1}} out of {{Math.ceil(filteredData.length / rowsPerPage)}}</p>
4  <button>Next</button>
5</div>

要在页面之间切换,请为我们的按钮添加点击事件处理器。 @click=movePages(-1) 为返回按钮和 @click=movePages(1) 为下一个按钮。

此方法会计算页面上第一行的行数:

1movePages: function(amount) {
2  let newStartRow = this.startRow + (amount * this.rowsPerPage);
3  if (newStartRow >= 0 && newStartRow < this.filteredData.length) {
4    this.startRow = newStartRow;
5  }
6}

现在添加一个计算属性,将过滤并返回当前页面的行:

1dataPerPage: function() {
2  return this.filteredData.filter((item, index) => index >= this.startRow && index < (this.startRow + this.rowsPerPage))
3}

我们现在必须显示更新的数据,因为我们能够在页面之间移动。

1<tr v-for="entry in dataPerPage">
2  <td v-for="key in columns"> {{entry[key]}}</td>
3</tr>

长度选择

要允许设置行数,添加一个简单的选择,其中 v 模型等于行PerPage 属性。

对于选项,我们会将 pageSizeMenu 属性设置为具有数值的数组:

1pageSizeMenu: [10, 20, 50, 100]
1<select v-model="rowsPerPage">
2  <option v-for="pageSize in pageSizeMenu" :value="pageSize">{{pageSize}}</option>
3</select>

这是你所需要的,才能改变每页的行数.Vue将完成其余的工作,由于双向数据绑定。

Published At
Categories with 技术
Tagged with
comments powered by Disqus