運行環境
iview版本: 3.5.0-rc.1
vue版本: 2.6.10
問題
iview中的table是不能直接佔滿父容器的,它的 height
屬性必須設置成一個固定值。假如直接給table設置style爲height:100%
,它的滾動條會丟失。
但是很多時候我們需要table佔滿父容器,需要表頭和頁腳進行固定,只讓中間數據部分進行滾動。
在 chrome
中打開 Elements
,可以看到 iview使用了 object
標籤。 html中一般只能監聽 window
的 resize
事件,而無法直接監聽 div
等元素大小改變的,但是有時可以插入一個 object
標籤來間接監聽 div
大小的改變。它這裏是否用了object
來監聽大小改變呢?
打開 iview的源碼 ,看到它果真是用來監聽大小改變的,但是它監聽改變後只處理了 width
,沒有處理 height
。所以這裏我們可以包裝一下 Table,監聽它的resize事件,然後動態設置 height屬性,來使 Table支持 height:100%
。
解決方法
iview是用的 element-resize-detector
庫來監聽外層div的改變,並且把將這個庫設置爲組件的全局變量。所以我們也可以使用這個全局變量來監聽外層div大小,然後動態設置 height。
創建一個 FillTable的組件,用來包裝 Table, FillTable.js
的源碼如下:
FillTable
import { Table } from 'iview';
export default {
name: 'fill-table',
render(h) {
/**傳遞prop */
const tableProps = {};
for (let prop in Table.props) {
tableProps[prop] = this[prop];
}
tableProps.height = this.tableHeight;
return h(Table, {
props: tableProps,
ref: 'table',
/**傳遞事件 */
on: this.$listeners,
/**傳遞作用域插槽 */
scopedSlots: this.$scopedSlots,
/**傳遞插槽 */
slot: this.$slot
});
},
props: (() => {
var props = {};
Object.assign(props, Table.props, {
height: {
type: Number
},
/** 默認佔滿父容器 */
fill: {
type: Boolean,
default: true
}
});
return props;
})(),
watch: {
height: {
handler() {
this.tableHeight = this.height;
}
},
immediate: true
},
data() {
// 自帶屬性值
return {
tableHeight: 0
};
},
methods: {
handleIViewTableResize(el) {
this.tableHeight = el.offsetHeight;
},
getTableRef() {
return this.$refs.table;
}
},
mounted() {
if (this.fill) {
// this.$nextTick(() => {
this.getTableRef().observer.listenTo(this.$el, this.handleIViewTableResize);
// });
}
/**傳遞方法 */
for (let method in Table.methods) {
this[method] = (...args) => Table.methods[method].apply(this.getTableRef(), args);
}
},
beforeDestroy() {
if (this.fill) {
this.getTableRef().observer.removeListener(this.$el, this.handleIViewTableResize);
}
}
};
使用
使用時和iview中的table的屬性和方法基本一致,只有以下地方需要注意:
- 添加 v-bind:fill屬性,默認值是true,爲true請不要再設置v-bind:height。設置爲true,則支持 table的style中的
height:100%
的樣式。 - 假如需要
v-bind:height=固定值
,則需要v-bind:fill="false"
- 假如需要找到table組件實例,請使用
getTableRef
方法,如this.refs.table.getTableRef()
。
<FillTable
ref="table"
style="width:100%;height:100%"
:columns="columns1"
:data="data1"
:fill="true"
>
<template #name="{ row }">
<strong>{{ row.name }}--ddd</strong>
</template>
</FillTable>
注意事項
由於是侵入性修改,假如iview改了源碼,比如將observer的全局字段去除了,則這裏就會出錯。這時可以直接引用 element-resize-detector
來監聽Table的大小改變。
demo
在線運行: https://meteor199.github.io/my-demo/vue/iview-fill-table/dist/index.html
demo源碼:https://github.com/meteor199/my-demo/tree/master/vue/iview-fill-table