123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712 |
- <template>
- <div class="el-table"
- :class="[{
- 'el-table--fit': fit,
- 'el-table--striped': stripe,
- 'el-table--border': border || isGroup,
- 'el-table--hidden': isHidden,
- 'el-table--group': isGroup,
- 'el-table--fluid-height': maxHeight,
- 'el-table--scrollable-x': layout.scrollX,
- 'el-table--scrollable-y': layout.scrollY,
- 'el-table--enable-row-hover': !store.states.isComplex,
- 'el-table--enable-row-transition': (store.states.data || []).length !== 0 && (store.states.data || []).length < 100
- }, tableSize ? `el-table--${ tableSize }` : '']"
- @mouseleave="handleMouseLeave($event)">
- <div class="hidden-columns" ref="hiddenColumns"><slot></slot></div>
- <div
- v-if="showHeader"
- v-mousewheel="handleHeaderFooterMousewheel"
- class="el-table__header-wrapper"
- ref="headerWrapper">
- <table-header
- ref="tableHeader"
- :store="store"
- :border="border"
- :default-sort="defaultSort"
- :style="{
- width: layout.bodyWidth ? layout.bodyWidth + 'px' : ''
- }">
- </table-header>
- </div>
- <div
- class="el-table__body-wrapper"
- ref="bodyWrapper"
- :class="[layout.scrollX ? `is-scrolling-${scrollPosition}` : 'is-scrolling-none']"
- :style="[bodyHeight]">
- <table-body
- :context="context"
- :store="store"
- :stripe="stripe"
- :row-class-name="rowClassName"
- :row-style="rowStyle"
- :highlight="highlightCurrentRow"
- :style="{
- width: bodyWidth
- }">
- </table-body>
- <div
- v-if="!data || data.length === 0"
- class="el-table__empty-block"
- ref="emptyBlock"
- :style="emptyBlockStyle">
- <span class="el-table__empty-text" >
- <slot name="empty">{{ emptyText || t('el.table.emptyText') }}</slot>
- </span>
- </div>
- <div
- v-if="$slots.append"
- class="el-table__append-wrapper"
- ref="appendWrapper">
- <slot name="append"></slot>
- </div>
- </div>
- <div
- v-if="showSummary"
- v-show="data && data.length > 0"
- v-mousewheel="handleHeaderFooterMousewheel"
- class="el-table__footer-wrapper"
- ref="footerWrapper">
- <table-footer
- :store="store"
- :border="border"
- :sum-text="sumText || t('el.table.sumText')"
- :summary-method="summaryMethod"
- :default-sort="defaultSort"
- :style="{
- width: layout.bodyWidth ? layout.bodyWidth + 'px' : ''
- }">
- </table-footer>
- </div>
- <div
- v-if="fixedColumns.length > 0"
- v-mousewheel="handleFixedMousewheel"
- class="el-table__fixed"
- ref="fixedWrapper"
- :style="[{
- width: layout.fixedWidth ? layout.fixedWidth + 'px' : ''
- },
- fixedHeight]">
- <div
- v-if="showHeader"
- class="el-table__fixed-header-wrapper"
- ref="fixedHeaderWrapper" >
- <table-header
- ref="fixedTableHeader"
- fixed="left"
- :border="border"
- :store="store"
- :style="{
- width: bodyWidth
- }"></table-header>
- </div>
- <div
- class="el-table__fixed-body-wrapper"
- ref="fixedBodyWrapper"
- :style="[{
- top: layout.headerHeight + 'px'
- },
- fixedBodyHeight]">
- <table-body
- fixed="left"
- :store="store"
- :stripe="stripe"
- :highlight="highlightCurrentRow"
- :row-class-name="rowClassName"
- :row-style="rowStyle"
- :style="{
- width: bodyWidth
- }">
- </table-body>
- <div
- v-if="$slots.append"
- class="el-table__append-gutter"
- :style="{ height: layout.appendHeight + 'px'}"></div>
- </div>
- <div
- v-if="showSummary"
- v-show="data && data.length > 0"
- class="el-table__fixed-footer-wrapper"
- ref="fixedFooterWrapper">
- <table-footer
- fixed="left"
- :border="border"
- :sum-text="sumText || t('el.table.sumText')"
- :summary-method="summaryMethod"
- :store="store"
- :style="{
- width: bodyWidth
- }"></table-footer>
- </div>
- </div>
- <div
- v-if="rightFixedColumns.length > 0"
- v-mousewheel="handleFixedMousewheel"
- class="el-table__fixed-right"
- ref="rightFixedWrapper"
- :style="[{
- width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : '',
- right: layout.scrollY ? (border ? layout.gutterWidth : (layout.gutterWidth || 0)) + 'px' : ''
- },
- fixedHeight]">
- <div v-if="showHeader"
- class="el-table__fixed-header-wrapper"
- ref="rightFixedHeaderWrapper">
- <table-header
- ref="rightFixedTableHeader"
- fixed="right"
- :border="border"
- :store="store"
- :style="{
- width: bodyWidth
- }"></table-header>
- </div>
- <div
- class="el-table__fixed-body-wrapper"
- ref="rightFixedBodyWrapper"
- :style="[{
- top: layout.headerHeight + 'px'
- },
- fixedBodyHeight]">
- <table-body
- fixed="right"
- :store="store"
- :stripe="stripe"
- :row-class-name="rowClassName"
- :row-style="rowStyle"
- :highlight="highlightCurrentRow"
- :style="{
- width: bodyWidth
- }">
- </table-body>
- <div
- v-if="$slots.append"
- class="el-table__append-gutter"
- :style="{ height: layout.appendHeight + 'px' }"></div>
- </div>
- <div
- v-if="showSummary"
- v-show="data && data.length > 0"
- class="el-table__fixed-footer-wrapper"
- ref="rightFixedFooterWrapper">
- <table-footer
- fixed="right"
- :border="border"
- :sum-text="sumText || t('el.table.sumText')"
- :summary-method="summaryMethod"
- :store="store"
- :style="{
- width: bodyWidth
- }"></table-footer>
- </div>
- </div>
- <div
- v-if="rightFixedColumns.length > 0"
- class="el-table__fixed-right-patch"
- ref="rightFixedPatch"
- :style="{
- width: layout.scrollY ? layout.gutterWidth + 'px' : '0',
- height: layout.headerHeight + 'px'
- }"></div>
- <div class="el-table__column-resize-proxy" ref="resizeProxy" v-show="resizeProxyVisible"></div>
- </div>
- </template>
- <script type="text/babel">
- import ElCheckbox from 'element-ui/packages/checkbox';
- import { debounce, throttle } from 'throttle-debounce';
- import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
- import Mousewheel from 'element-ui/src/directives/mousewheel';
- import Locale from 'element-ui/src/mixins/locale';
- import Migrating from 'element-ui/src/mixins/migrating';
- import { createStore, mapStates } from './store/helper';
- import TableLayout from './table-layout';
- import TableBody from './table-body';
- import TableHeader from './table-header';
- import TableFooter from './table-footer';
- import { parseHeight } from './util';
- let tableIdSeed = 1;
- export default {
- name: 'ElTable',
- mixins: [Locale, Migrating],
- directives: {
- Mousewheel
- },
- props: {
- data: {
- type: Array,
- default: function() {
- return [];
- }
- },
- size: String,
- width: [String, Number],
- height: [String, Number],
- maxHeight: [String, Number],
- fit: {
- type: Boolean,
- default: true
- },
- stripe: Boolean,
- border: Boolean,
- rowKey: [String, Function],
- context: {},
- showHeader: {
- type: Boolean,
- default: true
- },
- showSummary: Boolean,
- sumText: String,
- summaryMethod: Function,
- rowClassName: [String, Function],
- rowStyle: [Object, Function],
- cellClassName: [String, Function],
- cellStyle: [Object, Function],
- headerRowClassName: [String, Function],
- headerRowStyle: [Object, Function],
- headerCellClassName: [String, Function],
- headerCellStyle: [Object, Function],
- highlightCurrentRow: Boolean,
- highlightSelectionRow: {
- type: Boolean,
- default: false
- },
- currentRowKey: [String, Number],
- emptyText: String,
- expandRowKeys: Array,
- defaultExpandAll: Boolean,
- defaultSort: Object,
- tooltipEffect: String,
- spanMethod: Function,
- selectOnIndeterminate: {
- type: Boolean,
- default: true
- },
- indent: {
- type: Number,
- default: 16
- },
- treeProps: {
- type: Object,
- default() {
- return {
- hasChildren: 'hasChildren',
- children: 'children'
- };
- }
- },
- lazy: Boolean,
- load: Function
- },
- components: {
- TableHeader,
- TableFooter,
- TableBody,
- ElCheckbox
- },
- methods: {
- getMigratingConfig() {
- return {
- events: {
- expand: 'expand is renamed to expand-change'
- }
- };
- },
- setCurrentRow(row) {
- this.store.commit('setCurrentRow', row);
- },
- toggleRowSelection(row, selected) {
- this.store.toggleRowSelection(row, selected, false);
- this.store.updateAllSelected();
- },
- toggleRowExpansion(row, expanded) {
- this.store.toggleRowExpansionAdapter(row, expanded);
- },
- clearSelection() {
- this.store.clearSelection();
- },
- clearFilter(columnKeys) {
- this.store.clearFilter(columnKeys);
- },
- clearSort() {
- this.store.clearSort();
- },
- handleMouseLeave() {
- this.store.commit('setHoverRow', null);
- if (this.hoverState) this.hoverState = null;
- },
- updateScrollY() {
- const changed = this.layout.updateScrollY();
- if (changed) {
- this.layout.notifyObservers('scrollable');
- this.layout.updateColumnsWidth();
- }
- },
- handleFixedMousewheel(event, data) {
- const bodyWrapper = this.bodyWrapper;
- if (Math.abs(data.spinY) > 0) {
- const currentScrollTop = bodyWrapper.scrollTop;
- if (data.pixelY < 0 && currentScrollTop !== 0) {
- event.preventDefault();
- }
- if (data.pixelY > 0 && bodyWrapper.scrollHeight - bodyWrapper.clientHeight > currentScrollTop) {
- event.preventDefault();
- }
- bodyWrapper.scrollTop += Math.ceil(data.pixelY / 5);
- } else {
- bodyWrapper.scrollLeft += Math.ceil(data.pixelX / 5);
- }
- },
- handleHeaderFooterMousewheel(event, data) {
- const { pixelX, pixelY } = data;
- if (Math.abs(pixelX) >= Math.abs(pixelY)) {
- this.bodyWrapper.scrollLeft += data.pixelX / 5;
- }
- },
- // TODO 使用 CSS transform
- syncPostion() {
- const { scrollLeft, scrollTop, offsetWidth, scrollWidth } = this.bodyWrapper;
- const { headerWrapper, footerWrapper, fixedBodyWrapper, rightFixedBodyWrapper } = this.$refs;
- if (headerWrapper) headerWrapper.scrollLeft = scrollLeft;
- if (footerWrapper) footerWrapper.scrollLeft = scrollLeft;
- if (fixedBodyWrapper) fixedBodyWrapper.scrollTop = scrollTop;
- if (rightFixedBodyWrapper) rightFixedBodyWrapper.scrollTop = scrollTop;
- const maxScrollLeftPosition = scrollWidth - offsetWidth - 1;
- if (scrollLeft >= maxScrollLeftPosition) {
- this.scrollPosition = 'right';
- } else if (scrollLeft === 0) {
- this.scrollPosition = 'left';
- } else {
- this.scrollPosition = 'middle';
- }
- },
- throttleSyncPostion: throttle(16, function() {
- this.syncPostion();
- }),
- onScroll(evt) {
- let raf = window.requestAnimationFrame;
- if (!raf) {
- this.throttleSyncPostion();
- } else {
- raf(this.syncPostion);
- }
- },
- bindEvents() {
- this.bodyWrapper.addEventListener('scroll', this.onScroll, { passive: true });
- if (this.fit) {
- addResizeListener(this.$el, this.resizeListener);
- }
- },
- unbindEvents() {
- this.bodyWrapper.removeEventListener('scroll', this.onScroll, { passive: true });
- if (this.fit) {
- removeResizeListener(this.$el, this.resizeListener);
- }
- },
- resizeListener() {
- if (!this.$ready) return;
- let shouldUpdateLayout = false;
- const el = this.$el;
- const { width: oldWidth, height: oldHeight } = this.resizeState;
- const width = el.offsetWidth;
- if (oldWidth !== width) {
- shouldUpdateLayout = true;
- }
- const height = el.offsetHeight;
- if ((this.height || this.shouldUpdateHeight) && oldHeight !== height) {
- shouldUpdateLayout = true;
- }
- if (shouldUpdateLayout) {
- this.resizeState.width = width;
- this.resizeState.height = height;
- this.doLayout();
- }
- },
- doLayout() {
- if (this.shouldUpdateHeight) {
- this.layout.updateElsHeight();
- }
- this.layout.updateColumnsWidth();
- },
- sort(prop, order) {
- this.store.commit('sort', { prop, order });
- },
- toggleAllSelection() {
- this.store.commit('toggleAllSelection');
- }
- },
- computed: {
- tableSize() {
- return this.size || (this.$ELEMENT || {}).size;
- },
- bodyWrapper() {
- return this.$refs.bodyWrapper;
- },
- shouldUpdateHeight() {
- return this.height ||
- this.maxHeight ||
- this.fixedColumns.length > 0 ||
- this.rightFixedColumns.length > 0;
- },
- bodyWidth() {
- const { bodyWidth, scrollY, gutterWidth } = this.layout;
- return bodyWidth ? bodyWidth - (scrollY ? gutterWidth : 0) + 'px' : '';
- },
- bodyHeight() {
- const { headerHeight = 0, bodyHeight, footerHeight = 0} = this.layout;
- if (this.height) {
- return {
- height: bodyHeight ? bodyHeight + 'px' : ''
- };
- } else if (this.maxHeight) {
- const maxHeight = parseHeight(this.maxHeight);
- if (typeof maxHeight === 'number') {
- return {
- 'max-height': (maxHeight - footerHeight - (this.showHeader ? headerHeight : 0)) + 'px'
- };
- }
- }
- return {};
- },
- fixedBodyHeight() {
- if (this.height) {
- return {
- height: this.layout.fixedBodyHeight ? this.layout.fixedBodyHeight + 'px' : ''
- };
- } else if (this.maxHeight) {
- let maxHeight = parseHeight(this.maxHeight);
- if (typeof maxHeight === 'number') {
- maxHeight = this.layout.scrollX ? maxHeight - this.layout.gutterWidth : maxHeight;
- if (this.showHeader) {
- maxHeight -= this.layout.headerHeight;
- }
- maxHeight -= this.layout.footerHeight;
- return {
- 'max-height': maxHeight + 'px'
- };
- }
- }
- return {};
- },
- fixedHeight() {
- if (this.maxHeight) {
- if (this.showSummary) {
- return {
- bottom: 0
- };
- }
- return {
- bottom: (this.layout.scrollX && this.data.length) ? this.layout.gutterWidth + 'px' : ''
- };
- } else {
- if (this.showSummary) {
- return {
- height: this.layout.tableHeight ? this.layout.tableHeight + 'px' : ''
- };
- }
- return {
- height: this.layout.viewportHeight ? this.layout.viewportHeight + 'px' : ''
- };
- }
- },
- emptyBlockStyle() {
- if (this.data && this.data.length) return null;
- let height = '100%';
- if (this.layout.appendHeight) {
- height = `calc(100% - ${this.layout.appendHeight}px)`;
- }
- return {
- width: this.bodyWidth,
- height
- };
- },
- ...mapStates({
- selection: 'selection',
- columns: 'columns',
- tableData: 'data',
- fixedColumns: 'fixedColumns',
- rightFixedColumns: 'rightFixedColumns'
- })
- },
- watch: {
- height: {
- immediate: true,
- handler(value) {
- this.layout.setHeight(value);
- }
- },
- maxHeight: {
- immediate: true,
- handler(value) {
- this.layout.setMaxHeight(value);
- }
- },
- currentRowKey: {
- immediate: true,
- handler(value) {
- if (!this.rowKey) return;
- this.store.setCurrentRowKey(value);
- }
- },
- data: {
- immediate: true,
- handler(value) {
- this.store.commit('setData', value);
- }
- },
- expandRowKeys: {
- immediate: true,
- handler(newVal) {
- if (newVal) {
- this.store.setExpandRowKeysAdapter(newVal);
- }
- }
- }
- },
- created() {
- this.tableId = 'el-table_' + tableIdSeed++;
- this.debouncedUpdateLayout = debounce(50, () => this.doLayout());
- },
- mounted() {
- this.bindEvents();
- this.store.updateColumns();
- this.doLayout();
- this.resizeState = {
- width: this.$el.offsetWidth,
- height: this.$el.offsetHeight
- };
- // init filters
- this.store.states.columns.forEach(column => {
- if (column.filteredValue && column.filteredValue.length) {
- this.store.commit('filterChange', {
- column,
- values: column.filteredValue,
- silent: true
- });
- }
- });
- this.$ready = true;
- },
- destroyed() {
- this.unbindEvents();
- },
- data() {
- const { hasChildren = 'hasChildren', children = 'children' } = this.treeProps;
- this.store = createStore(this, {
- rowKey: this.rowKey,
- defaultExpandAll: this.defaultExpandAll,
- selectOnIndeterminate: this.selectOnIndeterminate,
- // TreeTable 的相关配置
- indent: this.indent,
- lazy: this.lazy,
- lazyColumnIdentifier: hasChildren,
- childrenColumnName: children
- });
- const layout = new TableLayout({
- store: this.store,
- table: this,
- fit: this.fit,
- showHeader: this.showHeader
- });
- return {
- layout,
- isHidden: false,
- renderExpanded: null,
- resizeProxyVisible: false,
- resizeState: {
- width: null,
- height: null
- },
- // 是否拥有多级表头
- isGroup: false,
- scrollPosition: 'left'
- };
- }
- };
- </script>
|