先安裝iview後在使用html
完善按鈕不顯示問題 ,當children過多時,點擊不動問題等數組
封裝iview
<template> <div :style="{width:tableWidth}" class='autoTbale'> <table class="table table-bordered" id='hl-tree-table'> <thead> <tr> <th v-for="(column,index) in cloneColumns" :key="index+100"> <label v-if="column.type === 'selection'"> <input type="checkbox" v-model="checks" @click="handleCheckAll"> </label> <label v-else> {{ renderHeader(column, index) }} <span class="ivu-table-sort" v-if="column.sortable"> <Icon type="md-arrow-dropup-circle" :class="{on: column._sortType === 'asc'}" @click.native="handleSort(index, 'asc')" /> <Icon type="md-arrow-dropdown-circle" :class="{on: column._sortType === 'desc'}" @click.native="handleSort(index, 'desc')" /> </span> </label> </th> </tr> </thead> <tbody> <tr v-for="(item,index) in initItems" :key="item.id" v-show="show(item)" :class="{'child-tr':item.parent}"> <td v-for="(column,snum) in columns" :key="column.key" :style=tdStyle(column)> <label v-if="column.type === 'selection'"> <input type="checkbox" class="itemCheck" :value="item.id" v-model="checkGroup" @click="handleCheckClick(item,$event,index)"> </label> <div v-if="column.type === 'action'"> <i-button :type="action.type" size="small" v-for='action in (column.actions)' @click="RowClick(item,$event,index,action.text,action.id)" :key="action.text"> {{action.text}} </i-button> </div> <label @click="toggle(index,item)" v-if="!column.type"> <span v-if='snum==iconRow()'> <i v-html='item.spaceHtml'></i> <!-- <i v-if="item.children&&item.children.length>0" class="ivu-icon" :class="{'ivu-icon-plus-circled':!item.expanded,'ivu-icon-minus-circled':item.expanded }"></i> --> <Icon v-if="item.children&&item.children.length>0" class="ivu-icon" :type="!item.expanded? 'md-arrow-dropright':'md-arrow-dropdown'" /> <i v-else class="ms-tree-space"></i> </span> {{renderBody(item,column) }} </label> </td> </tr> </tbody> </table> </div> </template> <script> export default { name: 'treeGrid', props: { columns: Array, items: { type: Array, default: function() { return []; } } }, data() { return { initItems: [], //處理後數據數組 cloneColumns: [], //處理後的表頭數據 checkGroup: [], //複選框數組 checks: false, //全選 screenWidth: document.body.clientWidth, //自適應寬 tdsWidth: 0, //td總寬 timer: false, //控制監聽時長 dataLength: 0, //樹形數據長度 } }, computed: { tableWidth() { return this.tdsWidth > this.screenWidth && this.screenWidth > 0 ? this.screenWidth + 'px' : '100%' } }, watch: { screenWidth(val) { if (!this.timer) { this.screenWidth = val this.timer = true setTimeout(() => { this.timer = false }, 400) } }, items() { if (this.items) { this.dataLength = this.Length(this.items) this.initData(this.deepCopy(this.items), 1, null); this.checkGroup = this.renderCheck(this.items) if (this.checkGroup.length == this.dataLength) { this.checks = true } else { this.checks = false } } }, columns: { handler() { this.cloneColumns = this.makeColumns(); }, deep: true }, checkGroup(data) { this.checkAllGroupChange(data) }, }, mounted() { if (this.items) { this.dataLength = this.Length(this.items) this.initData(this.deepCopy(this.items), 1, null); this.cloneColumns = this.makeColumns(); this.checkGroup = this.renderCheck(this.items) if (this.checkGroup.length == this.dataLength) { this.checks = true } else { this.checks = false } } // 綁定onresize事件 監聽屏幕變化設置寬 this.$nextTick(() => { this.screenWidth = document.body.clientWidth }) window.onresize = () => { return (() => { window.screenWidth = document.body.clientWidth this.screenWidth = window.screenWidth })() } }, methods: { // 有無多選框摺疊位置優化 iconRow() { for (var i = 0, len = this.columns.length; i < len; i++) { if (this.columns[i].type == 'selection') { return 1 } } return 0 }, // 設置td寬度,td的align tdStyle(column) { var style = {} if (column.align) { style["text-align"] = column.align; } if (column.width) { style["min-width"] = column.width + 'px'; } return style; }, // 排序事件 handleSort(index, type) { this.cloneColumns.forEach((col) => col._sortType = 'normal'); if (this.cloneColumns[index]._sortType === type) { this.cloneColumns[index]._sortType = 'normal' } else { this.cloneColumns[index]._sortType = type } this.$emit('on-sort-change', this.cloneColumns[index]['key'], this.cloneColumns[index]['_sortType']) }, // 點擊某一行事件 RowClick(data, event, index, text,action) { let result = this.makeData(data) this.$emit('on-row-click', result, event, index, text, action) }, // 點擊事件 返回數據處理 makeData(data) { const t = this.type(data); let o; if (t === 'array') { o = []; } else if (t === 'object') { o = {}; } else { return data; } if (t === 'array') { for (let i = 0; i < data.length; i++) { o.push(this.makeData(data[i])); } } else if (t === 'object') { for (let i in data) { if (i != 'spaceHtml' && i != 'parent' && i != 'level' && i != 'expanded' && i != 'isShow' && i != 'load') { o[i] = this.makeData(data[i]); } } } return o; }, // 處理表頭數據 makeColumns() { let columns = this.deepCopy(this.columns); this.tdsWidth = 0 columns.forEach((column, index) => { column._index = index; column._width = column.width ? column.width : ''; column._sortType = 'normal'; this.tdsWidth += column.width ? parseFloat(column.width) : 0; }); return columns; }, // 數據處理 增長自定義屬性監聽 initData(items, level, parent) { this.initItems = [] let spaceHtml = ""; for (var i = 1; i < level; i++) { spaceHtml += "<i class='ms-tree-space'></i>" } items.forEach((item, index) => { item = Object.assign({}, item, { "parent": parent, "level": level, "spaceHtml": spaceHtml }); if ((typeof item.expanded) == "undefined") { item = Object.assign({}, item, { "expanded": false }); } if ((typeof item.show) == "undefined") { item = Object.assign({}, item, { "isShow": false }); } if ((typeof item.isChecked) == "undefined") { item = Object.assign({}, item, { "isChecked": false }); } item = Object.assign({}, item, { "load": (item.expanded ? true : false) }); this.initItems.push(item); if (item.children && item.expanded) { this.initData(item.children, level + 1, item); } }) }, // 隱藏顯示 show(item) { return ((item.level == 1) || (item.parent && item.parent.expanded && item.isShow)); }, toggle(index, item) { let level = item.level + 1; let spaceHtml = ""; for (var i = 1; i < level; i++) { spaceHtml += "<i class='ms-tree-space'></i>" } if (item.children) { if (item.expanded) { item.expanded = !item.expanded; this.close(index, item); } else { item.expanded = !item.expanded; if (item.load) { this.open(index, item); } else { item.load = true; item.children.forEach((child, childIndex) => { this.initItems.splice((index + childIndex + 1), 0, child); //設置監聽屬性 this.$set(this.initItems[index + childIndex + 1], 'parent', item); this.$set(this.initItems[index + childIndex + 1], 'level', level); this.$set(this.initItems[index + childIndex + 1], 'spaceHtml', spaceHtml); this.$set(this.initItems[index + childIndex + 1], 'isShow', true); this.$set(this.initItems[index + childIndex + 1], 'expanded', false); }) } } } }, open(index, item) { if (item.children) { item.children.forEach((child, childIndex) => { child.isShow = true; if (child.children && child.expanded) { this.open(index + childIndex + 1, child); } }) } }, close(index, item) { if (item.children) { item.children.forEach((child, childIndex) => { child.isShow = false; child.expanded = false; if (child.children) { this.close(index + childIndex + 1, child); } }) } }, //點擊check勾選框,判斷是否有children節點 若是有就一併勾選 handleCheckClick(data, event, index){ data.isChecked = !data.isChecked; var arr = data.children; if(arr){ if(data.isChecked){ this.checkGroup.push(data.id); for (let i=0; i<arr.length; i++){ this.checkGroup.push(arr[i].id) } }else { for (var i=0; i<this.checkGroup.length; i++){ if(this.checkGroup[i] == data.id){ this.checkGroup.splice(i,1) } for (var j=0; j<arr.length; j++){ if(this.checkGroup[i] == arr[j].id){ this.checkGroup.splice(i,1); } } } } } }, //checkbox 全選 選擇事件 handleCheckAll() { this.checks = !this.checks; if (this.checks) { this.checkGroup = this.getArray(this.checkGroup.concat(this.All(this.items))) } else { this.checkGroup = [] } // this.$emit('on-selection-change', this.checkGroup) }, // 數組去重 getArray(a) { var hash = {}, len = a.length, result = []; for (var i = 0; i < len; i++) { if (!hash[a[i]]) { hash[a[i]] = true; result.push(a[i]); } } return result; }, checkAllGroupChange(data) { if (this.dataLength > 0 && data.length === this.dataLength) { this.checks = true; } else { this.checks = false; } this.$emit('on-selection-change', this.checkGroup) }, All(data) { let arr = [] data.forEach((item) => { arr.push(item.id) if (item.children && item.children.length > 0) { arr = arr.concat(this.All(item.children)); } }) return arr }, // 返回樹形數據長度 Length(data) { let length = data.length data.forEach((child) => { if (child.children) { length += this.Length(child.children) } }) return length; }, // 返回表頭 renderHeader(column, $index) { if ('renderHeader' in this.columns[$index]) { return this.columns[$index].renderHeader(column, $index); } else { return column.title || '#'; } }, // 返回內容 renderBody(row, column, index) { return row[column.key] }, // 默認選中 renderCheck(data) { let arr = [] data.forEach((item) => { if (item._checked) { arr.push(item.id) } if (item.children && item.children.length > 0) { arr = arr.concat(this.renderCheck(item.children)); } }) return arr }, // 深度拷貝函數 deepCopy(data) { var t = this.type(data), o, i, ni; if (t === 'array') { o = []; } else if (t === 'object') { o = {}; } else { return data; } if (t === 'array') { for (i = 0, ni = data.length; i < ni; i++) { o.push(this.deepCopy(data[i])); } return o; } else if (t === 'object') { for (i in data) { o[i] = this.deepCopy(data[i]); } return o; } }, type(obj) { var toString = Object.prototype.toString; var map = { '[object Boolean]': 'boolean', '[object Number]': 'number', '[object String]': 'string', '[object Function]': 'function', '[object Array]': 'array', '[object Date]': 'date', '[object RegExp]': 'regExp', '[object Undefined]': 'undefined', '[object Null]': 'null', '[object Object]': 'object' }; return map[toString.call(obj)]; } }, beforeDestroy() { window.onresize = null } } </script> <style> .autoTbale { overflow: auto; } table { width: 100%; border-spacing: 0; border-collapse: collapse; } .table-bordered { border: 1px solid #EBEBEB; } .table>tbody>tr>td, .table>tbody>tr>th, .table>thead>tr>td, .table>thead>tr>th { border-top: 1px solid #e7eaec; line-height: 1.42857; padding: 8px; vertical-align: middle; } .table-bordered>tbody>tr>td, .table-bordered>tbody>tr>th, .table-bordered>tfoot>tr>td, .table-bordered>tfoot>tr>th, .table-bordered>thead>tr>td, .table-bordered>thead>tr>th { border: 1px solid #e7e7e7; } .table>thead>tr>th { border-bottom: 1px solid #DDD; } .table-bordered>thead>tr>td, .table-bordered>thead>tr>th { background-color: #F5F5F6; } #hl-tree-table>tbody>tr { background-color: #fbfbfb; } #hl-tree-table>tbody>.child-tr { background-color: #fff; } label { margin: 0 8px; } .ms-tree-space { position: relative; top: 1px; display: inline-block; font-style: normal; font-weight: 400; line-height: 1; width: 14px; height: 14px; } .ms-tree-space::before { content: "" } #hl-tree-table th>label { margin: 0; } </style>
使用
ide
<template> <div> <tree-grid :items='data' :columns='columns' @on-row-click='rowClick' @on-selection-change='selectionClick' @on-sort-change='sortClick' ></tree-grid> </div> </template> <script> import TreeGrid from '../components/TableGrid' export default { name: "TableGrid", components: { TreeGrid }, data() { return { columns: [ { type: 'selection', width: '50', }, { title: '編碼', key: 'code', sortable: true, width: '150', }, { title: '名稱', key: 'name', width: '150', }, { title: '狀態', key: 'status', width: '150', }, { title: '備註', key: 'remark', width: '150', }, { title: '操做', type: 'action', actions: [{ type: 'primary', id:'edit', // 用於區分編輯和刪除,在打印時使用 text: '編輯' }, { type: 'error', id: 'del', text: '刪除' }], width: '150', } ], data: [ { id: '1', code: '1', name: '測試數據1', status: '啓用', remark: '測試數據測試數據', _checked: true }, { id: '2', code: '2', name: '測試數據2', status: '啓用', remark: '測試數據測試數據', children: [ { id: '01', code: '21', name: '21', status: '啓用', remark: '測試數據測試數據', children: [{ id: '211', code: '211', name: '測試數據022', status: '啓用', remark: '測試數據測試數據', children: [{ id: '2111', code: '2111', name: '測試數據022', status: '啓用', remark: '測試數據測試數據', }] }, { id: '212', code: '212', name: '測試數據023', status: '啓用', remark: '測試數據測試數據', }] }, { id: '02', code: '22', name: '測試數據02', status: '啓用', remark: '測試數據測試數據', } ] }, { id: '3', code: '3', name: '測試數據3', status: '啓用', remark: '測試數據測試數據' }, { id: '4', code: '4', name: '測試數據4', status: '啓用', remark: '測試數據測試數據' } ] } }, methods: { rowClick(data, event,index,text,action) { console.log('當前點擊: ' , action ) console.log('當前行數據: ' , data ) console.log('點擊行號: ' , index ) console.log('點擊事件: ' , event ) }, selectionClick(arr) { console.log('選中數據id數組:' , arr) }, sortClick(key, type) { console.log( 'asc:正序,desc:降序' ) console.log('排序字段:' , key) console.log('排序規則:' , type) } } } </script>