【Vue.JS】Vue.JS 表格內容排序組件,Render函數的使用

聲明:文中代碼整體思路來源於 樑灝 編著的 【Vue.JS 實戰】一書,學習過程中因覺得該組件效果不錯,比較實用,所以記錄一份並做了詳細的註釋以供學習

效果圖

pic

代碼

  • index.html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>表格內容排序組件</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="style.css" />
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
</head>

<body>
    <div id="demo" v-cloak>
        <v-table :thead="thead" :tbody="tbody"></v-table>
        <button @click="handleAddData">隨機添加一條數據</button>
    </div>
    <script src="table.js"></script>
    <script src="index.js"></script>
</body>

</html>
  • index.js
var demo = new Vue({
    el: '#demo',
    data: {
        //構造表頭
        thead: [{
                title: 'name',
                key: 'name',
            },
            {
                title: 'age',
                key: 'age',
                sortable: true,
            }, {
                title: 'birthday',
                key: 'birthday',
                sortable: true,
            }, {
                title: 'address',
                key: 'address',
            }
        ],
        //構造表數據
        tbody: [{
            name: '張三',
            age: 18,
            birthday: '1937-10-01',
            address: '深圳龍華區'
        }, {
            name: '李四',
            age: 20,
            birthday: '1999-05-18',
            address: '北京海淀區',
        }, {
            name: '王五',
            age: 23,
            birthday: '1996-02-15',
        }, ],
    },

    methods: {
        /* 添加按鈕觸發事件,隨機添加一筆數據 */
        handleAddData: function () {
            /*隨機漢字生成*/
            eval("var firstname=" + '" \\u' + (Math.round(Math.random() * 20901) + 19968).toString(16) + '"');
            eval("var lastname=" + '" \\u' + (Math.round(Math.random() * 20901) + 19968).toString(16) + '"');
            var starttime =
                this.tbody.push({
                    /* 構造兩個字的隨機用戶名 */
                    name: firstname + lastname,
                    /* 隨機數[0, 20]向上取整。新學習的寫法,現學現用 */
                    age: ~~(Math.random() * 20) + 1,
                    // birthday: '2019-06-10',
                    /* 隨機構造一個日期,因時間戳轉換代碼太過繁瑣,且這裏並非重點,只是爲了體現排序,所以用字符串構造(還會出現2月30日) */
                    birthday: (1989 + ~~(Math.random() * 20)) + '-' + (~~(Math.random() * 11) + 1) + '-' + (~~(Math.random() * 29) + 1),
                    address: '廣東河源',
                })
        }
    },
})
  • table.js
Vue.component('vTable', {
    /* 接受父組件參數 */
    props: {
        /* 表頭:Array 類型,默認爲空數組 */
        thead: {
            type: Array,
            default: function () {
                return [];
            }
        },
        /* 表身:Array 類型,默認爲空數組 */
        tbody: {
            type: Array,
            default: function () {
                return [];
            }
        }
    },
    /* 組件中的數據 */
    data() {
        return {
            /* 初始化表頭數組 */
            currentHead: [],
            /* 初始化表身數組 */
            currentBody: [],
        }
    },

    /* render 函數構造表格 */
    render: function (createElement) {
        /**
         * 定義 _this 臨時存儲 Vue 實例。
         */
        var _this = this;
        /* 定義數組。用來存儲表頭信息,含節點 */
        var ths = [];
        /* 遍歷表頭信息 */
        this.currentHead.forEach((ele, index) => {
            /* 如果當前列允許排序 即 sortable 爲 Truely */
            if (ele.sortable) {
                /**
                 * 構造節點並push 到 ths 數組
                 * 節點基本結構如下:
                 *  <th>
                 *      <span>ele.title</span>
                 *      <a class="on" @click="handleSortByAsc">↑</a>
                 *      <a class="on" @click="handleSortByDesc">↓</a>
                 *  </th>
                 */
                ths.push(createElement('th', [
                    createElement('span', ele.title),
                    createElement('a', {
                        class: {
                            on: ele._sortType === 'asc'
                        },
                        on: {
                            click: function () {
                                _this.handleSortByAsc(index);
                            }
                        }
                    }, '↑'),
                    createElement('a', {
                        class: {
                            on: ele._sortType === 'desc'
                        },
                        on: {
                            click: function () {
                                _this.handleSortByDesc(index);
                            }
                        }
                    }, '↓'),
                ]));
            /* 如果當前列不允許排序操作,即 sortable 爲 Falsely */
            } else {
                /**
                 * 節點基本結構如下:
                 * <th>
                 *      <span>ele.title</span>
                 * </th>
                 */
                ths.push(createElement('th', ele.title));
            }
        });

        /* 定義數組。用來存儲表身信息,含節點 */
        var trs = [];
        /* 遍歷表身信息 */
        this.currentBody.forEach((ele, index) => {
            /* 循環內定義臨時數組,存儲當前 tr 的數據 */
            var tds = [];
            /* 遍歷表頭,根據 key 值獲取當前 tr 對應的 td 的內容,並 push 到 tds 數組*/
            this.currentHead.forEach(cell => {
                tds.push(createElement('td', ele[cell.key]));
            });
            /* 將取到的 tr 數據 push 到 tr 數組中 */
            trs.push(createElement('tr', tds));
        });

        /**
         * 返回節點。
         * 節點基本結構如下:
         *  <table>
         *      <thead>
         *          <tr>
         *              ...ths數組的內容...
         *          </tr>
         *      </thead>
         *      <tbody>
         *          ...trs 數組的內容...
         *      </tbody>
         *  </table>
         */
        return createElement('table', [
            createElement('thead', [
                createElement('tr', ths)
            ]),
            createElement('tbody', trs)
        ])
    },

    methods: {
        /* 初始化組件中表頭數組,數據源來自父組件 */
        makeHead: function () {
            this.currentHead = this.thead.map((col, index) => {
                /* 默認排序類型爲 normal */
                col._sortType = 'normal';
                col._index = index;
                return col;
            });
        },
        /* 初始化組件中表身數組,數據源來自父組件 */
        makeBody: function () {
            this.currentBody = this.tbody.map((row, index) => {
                row._index = index;
                return row;
            })
        },
        /* 正序排列 從小到大 */
        handleSortByAsc: function (index) {
            /* 獲取當前點擊列的 key 值 */
            var key = this.currentHead[index].key;
            /* 將表頭所有列的排序類型置爲默認 normal */
            this.currentHead.forEach(col => {
                col._sortType = 'normal';
            });
            /* 修改當前點擊列的排序類型爲 asc */
            this.currentHead[index]._sortType = 'asc';
            /* 對錶身數組進行正序排列 */
            this.currentBody.sort((a, b) => {
                return a[key] > b[key] ? 1 : -1;
            })
        },
        /* 倒序排列 從大到小 */
        handleSortByDesc: function (index) {
            /* 獲取當前點擊列的 key 值 */
            var key = this.currentHead[index].key;
            /* 將表頭所有列的排序類型置爲默認 normal */
            this.currentHead.forEach(col => {
                col._sortType = 'normal';
            });
            /* 修改當前點擊列的排序類型爲 desc */
            this.currentHead[index]._sortType = 'desc';
            /* 對錶身數組進行倒序排列 */
            this.currentBody.sort((a, b) => {
                return a[key] < b[key] ? 1 : -1;
            })
        }
    },

    /* 定義監聽器 */
    watch: {
        /* 監聽表身數據。當表身數據發生變化時,觸發該事件 */
        tbody: function () {
            /* 初始化表身 */
            this.makeBody();
            /* 過濾排序類型不爲 normal 的表頭,即獲取指定了排序類型的字段 */
            var sortedColumn = this.currentHead.filter(col => {
                return col._sortType !== 'normal';
            });
            /* 如果指定了排序類型 */
            if (sortedColumn.length > 0) {
                if (sortedColumn[0]._sortType === 'asc') {
                    /* 正序排列 */
                    this.handleSortByAsc(sortedColumn[0]._index);
                } else {
                    /* 倒序排列 */
                    this.handleSortByDesc(sortedColumn[0]._index);
                }
            }
        }
    },

    /* 鉤子函數。當Vue實例掛載後執行 */
    mounted() {
        /* 初始化表頭 */
        this.makeHead();
        /* 初始化表身 */
        this.makeBody();
    },
})
  • style.css
[v-cloak] {
    display: none;
}

table {
    width: 100%;
    margin-bottom: 24px;
    border-collapse: collapse;
    border-spacing: 0;
    empty-cells: show;
    border: 1px solid #e9e9e9;
}

table th {
    background-color: #f7f7f7;
    color: #5c6b77;
    font-weight: 600;
    white-space: nowrap;
}

table th,
table td {
    padding: 8px 16px;
    border: 1px solid #e9e9e9;
    text-align: left;
}

table th a {
    display: inline-block;
    margin: 0 4px;
    cursor: pointer;
}

table th a.on {
    color: #3399FF;
}

table th a:hover {
    color: #3399FF;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章