mirror of https://github.com/ElemeFE/element
Table: add filter feature. (#684)
parent
5a6cca14c6
commit
14495f6189
|
@ -14,6 +14,7 @@
|
|||
- 修复 Switch 的 width 属性无效的问题
|
||||
- Table 增加 rowClassName 属性
|
||||
- TableColumn 增加 fixed 属性,可选值:true, false, left, right
|
||||
- TableColumn 增加属性:filters、filterMultiple、filterMethod、filteredValue
|
||||
- TableColumn[type="selection"] 增加 selectable 属性
|
||||
- 修复 Input textarea 在动态赋值时 autosize 没有触发的问题
|
||||
- 修复 Input Number min max 属性设置后点击加减出现的崩溃的bug
|
||||
|
|
|
@ -9,28 +9,32 @@
|
|||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
zip: 200333,
|
||||
tag: '家'
|
||||
}, {
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
zip: 200333,
|
||||
tag: '公司'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
zip: 200333,
|
||||
tag: '家'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
province: '上海',
|
||||
city: '普陀区',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
zip: 200333
|
||||
zip: 200333,
|
||||
tag: '公司'
|
||||
}],
|
||||
tableData2: [{
|
||||
date: '2016-05-02',
|
||||
|
@ -119,6 +123,10 @@
|
|||
return row.address;
|
||||
},
|
||||
|
||||
filterTag(value, row) {
|
||||
return row.tag === value;
|
||||
},
|
||||
|
||||
tableRowClassName(row, index) {
|
||||
if (index === 1) {
|
||||
return 'info-row';
|
||||
|
@ -810,6 +818,85 @@
|
|||
```
|
||||
:::
|
||||
|
||||
### 筛选
|
||||
|
||||
对表格进行筛选,可快速查找到自己想看的数据。
|
||||
|
||||
:::demo 在列中设置`filters``filter-method`属性即可开启该列的筛选,filters 是一个数组,`filter-method`是一个方法,它用于决定某些数据是否显示,会传入两个参数:`value`和`row`。
|
||||
```html
|
||||
<template>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
border
|
||||
style="width: 100%">
|
||||
<el-table-column
|
||||
prop="date"
|
||||
label="日期"
|
||||
sortable
|
||||
width="180">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="姓名"
|
||||
width="180">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="address"
|
||||
label="地址"
|
||||
:formatter="formatter">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="tag"
|
||||
label="标签"
|
||||
width="100"
|
||||
:filters="[{ text: '家', value: '家' }, { text: '公司', value: '公司' }]"
|
||||
:filter-method="filterTag"
|
||||
inline-template>
|
||||
<el-tag :type="row.tag === '家' ? 'primary' : 'success'" close-transition>{{row.tag}}</el-tag>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tableData: [{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄',
|
||||
tag: '家'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1517 弄',
|
||||
tag: '公司'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1519 弄',
|
||||
tag: '家'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1516 弄',
|
||||
tag: '公司'
|
||||
}]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatter(row, column) {
|
||||
return row.address;
|
||||
},
|
||||
filterTag(value, row) {
|
||||
return row.tag === value;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Table Attributes
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
||||
|
@ -853,3 +940,7 @@
|
|||
| align | 对齐方式 | String | left, center, right | left |
|
||||
| selectable | 仅对 type=selection 的列有效,类型为 Function,Function 的返回值用来决定这一行的 CheckBox 是否可以勾选 | Function(row, index) | - | - |
|
||||
| reserve-selection | 仅对 type=selection 的列有效,类型为 Boolean,为 true 则代表会保留之前数据的选项,需要配合 Table 的 clearSelection 方法使用。 | Boolean | - | false |
|
||||
| filters | 数据过滤的选项,数组格式,数组中的元素需要有 text 和 value 属性。 | Array[{ text, value }] | — | — |
|
||||
| filter-multiple | 数据过滤的选项是否多选 | Boolean | — | true |
|
||||
| filter-method | 数据过滤使用的方法,如果是多选的筛选项,对每一条数据会执行多次,任意一次返回 true 就会显示。 | Function(value, row) | — | — |
|
||||
| filteredValue | 选中的数据过滤项,如果需要自定义表头过滤的渲染方式,可能会需要此属性。 | Array | — | — |
|
|
@ -0,0 +1,27 @@
|
|||
var dropdowns = [];
|
||||
|
||||
document.addEventListener('click', function(event) {
|
||||
dropdowns.forEach(function(dropdown) {
|
||||
var target = event.target;
|
||||
if (!dropdown || !dropdown.$el) return;
|
||||
if (target === dropdown.$el || dropdown.$el.contains(target)) {
|
||||
return;
|
||||
}
|
||||
dropdown.handleOutsideClick && dropdown.handleOutsideClick(event);
|
||||
});
|
||||
});
|
||||
|
||||
export default {
|
||||
open(instance) {
|
||||
if (instance) {
|
||||
dropdowns.push(instance);
|
||||
}
|
||||
},
|
||||
|
||||
close(instance) {
|
||||
var index = dropdowns.indexOf(instance);
|
||||
if (index !== -1) {
|
||||
dropdowns.splice(instance, 1);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,180 @@
|
|||
<template>
|
||||
<transition name="md-fade-bottom">
|
||||
<div class="el-table-filter" v-if="multiple" v-show="showPopper">
|
||||
<div class="el-table-filter__content">
|
||||
<el-checkbox-group class="el-table-filter__checkbox-group" v-model="filteredValue">
|
||||
<el-checkbox
|
||||
v-for="filter in filters"
|
||||
:label="filter.value">{{ filter.text }}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<div class="el-table-filter__bottom">
|
||||
<button @click="handleConfirm"
|
||||
:class="{ 'is-disabled': filteredValue.length === 0 }"
|
||||
:disabled="filteredValue.length === 0">{{ $t('el.table.confirmFilter') }}</button>
|
||||
<button @click="handleReset">{{ $t('el.table.resetFilter') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="el-table-filter" v-else v-show="showPopper">
|
||||
<ul class="el-table-filter__list">
|
||||
<li class="el-table-filter__list-item"
|
||||
:class="{ 'is-active': !filterValue }"
|
||||
@click="handleSelect(null)">{{ $t('el.table.clearFilter') }}</li>
|
||||
<li class="el-table-filter__list-item"
|
||||
v-for="filter in filters"
|
||||
:label="filter.value"
|
||||
:class="{ 'is-active': isActive(filter) }"
|
||||
@click="handleSelect(filter.value)" >{{ filter.text }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script type="text/jsx">
|
||||
import Popper from 'element-ui/src/utils/vue-popper';
|
||||
import Locale from 'element-ui/src/mixins/locale';
|
||||
import Clickoutside from 'element-ui/src/utils/clickoutside';
|
||||
import Dropdown from './dropdown';
|
||||
import ElCheckbox from 'element-ui/packages/checkbox';
|
||||
import ElCheckboxGroup from 'element-ui/packages/checkbox-group';
|
||||
|
||||
export default {
|
||||
name: 'el-table-filter-panel',
|
||||
|
||||
mixins: [Popper, Locale],
|
||||
|
||||
directives: {
|
||||
Clickoutside
|
||||
},
|
||||
|
||||
components: {
|
||||
ElCheckbox,
|
||||
ElCheckboxGroup
|
||||
},
|
||||
|
||||
props: {
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottom-end'
|
||||
}
|
||||
},
|
||||
|
||||
customRender(h) {
|
||||
return (<div class="el-table-filter">
|
||||
<div class="el-table-filter__content">
|
||||
</div>
|
||||
<div class="el-table-filter__bottom">
|
||||
<button on-click={ this.handleConfirm }>{ this.$t('el.table.confirmFilter') }</button>
|
||||
<button on-click={ this.handleReset }>{ this.$t('el.table.resetFilter') }</button>
|
||||
</div>
|
||||
</div>);
|
||||
},
|
||||
|
||||
methods: {
|
||||
isActive(filter) {
|
||||
return filter.value === this.filterValue;
|
||||
},
|
||||
|
||||
handleOutsideClick() {
|
||||
this.showPopper = false;
|
||||
},
|
||||
|
||||
handleConfirm() {
|
||||
this.confirmFilter(this.filteredValue);
|
||||
this.handleOutsideClick();
|
||||
},
|
||||
|
||||
handleReset() {
|
||||
this.filteredValue = [];
|
||||
this.confirmFilter(this.filteredValue);
|
||||
this.handleOutsideClick();
|
||||
},
|
||||
|
||||
handleSelect(filterValue) {
|
||||
this.filterValue = filterValue;
|
||||
|
||||
if (filterValue) {
|
||||
this.confirmFilter(this.filteredValue);
|
||||
} else {
|
||||
this.confirmFilter([]);
|
||||
}
|
||||
|
||||
this.handleOutsideClick();
|
||||
},
|
||||
|
||||
confirmFilter(filteredValue) {
|
||||
this.table.store.commit('filterChange', {
|
||||
column: this.column,
|
||||
values: filteredValue
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
table: null,
|
||||
cell: null,
|
||||
column: null
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
filters() {
|
||||
return this.column && this.column.filters;
|
||||
},
|
||||
|
||||
filterValue: {
|
||||
get() {
|
||||
return (this.column.filteredValue || [])[0];
|
||||
},
|
||||
set(value) {
|
||||
if (this.filteredValue) {
|
||||
if (value) {
|
||||
this.filteredValue.splice(0, 1, value);
|
||||
} else {
|
||||
this.filteredValue.splice(0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
filteredValue: {
|
||||
get() {
|
||||
if (this.column) {
|
||||
return this.column.filteredValue || [];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
set(value) {
|
||||
if (this.column) {
|
||||
this.column.filteredValue = value;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
multiple() {
|
||||
if (this.column) {
|
||||
return this.column.filterMultiple;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.popperElm = this.$el;
|
||||
this.referenceElm = this.cell;
|
||||
this.table.$refs.bodyWrapper.addEventListener('scroll', () => {
|
||||
this.updatePopper();
|
||||
});
|
||||
|
||||
this.$watch('showPopper', (value) => {
|
||||
if (this.column) this.column.filterOpened = value;
|
||||
if (value) {
|
||||
Dropdown.open(this);
|
||||
} else {
|
||||
Dropdown.close(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -1,22 +1,4 @@
|
|||
import { getValueByPath, getCell } from './util';
|
||||
|
||||
const getColumnById = function(table, columnId) {
|
||||
let column = null;
|
||||
table.columns.forEach(function(item) {
|
||||
if (item.id === columnId) {
|
||||
column = item;
|
||||
}
|
||||
});
|
||||
return column;
|
||||
};
|
||||
|
||||
const getColumnByCell = function(table, cell) {
|
||||
const matches = (cell.className || '').match(/el-table_[^\s]+/gm);
|
||||
if (matches) {
|
||||
return getColumnById(table, matches[0]);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
import { getValueByPath, getCell, getColumnById, getColumnByCell } from './util';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
|
@ -19,21 +19,16 @@ const defaults = {
|
|||
minWidth: 48,
|
||||
realWidth: 48,
|
||||
direction: ''
|
||||
},
|
||||
filter: {
|
||||
headerTemplate: function(h) { return <span>filter header</span>; },
|
||||
direction: ''
|
||||
}
|
||||
};
|
||||
|
||||
const forced = {
|
||||
selection: {
|
||||
headerTemplate: function(h) {
|
||||
return <div><el-checkbox
|
||||
return <el-checkbox
|
||||
nativeOn-click={ this.toggleAllSelection }
|
||||
domProps-value={ this.isAllSelected }
|
||||
on-input={ (value) => { this.$emit('allselectedchange', value); } } />
|
||||
</div>;
|
||||
on-input={ (value) => { this.$emit('allselectedchange', value); } } />;
|
||||
},
|
||||
template: function(h, { row, column, store, $index }) {
|
||||
return <el-checkbox
|
||||
|
@ -47,7 +42,7 @@ const forced = {
|
|||
index: {
|
||||
// headerTemplate: function(h) { return <div>#</div>; },
|
||||
headerTemplate: function(h, label) {
|
||||
return <div>{ label || '#' }</div>;
|
||||
return label || '#';
|
||||
},
|
||||
template: function(h, { $index }) {
|
||||
return <div>{ $index + 1 }</div>;
|
||||
|
@ -56,7 +51,7 @@ const forced = {
|
|||
},
|
||||
filter: {
|
||||
headerTemplate: function(h) {
|
||||
return <div>#</div>;
|
||||
return '#';
|
||||
},
|
||||
template: function(h, { row, column }) {
|
||||
return <el-tag type="primary" style="height: 16px; line-height: 16px; min-width: 40px; text-align: center">{ row[column.property] }</el-tag>;
|
||||
|
@ -118,7 +113,13 @@ export default {
|
|||
fixed: [Boolean, String],
|
||||
formatter: Function,
|
||||
selectable: Function,
|
||||
reserveSelection: Boolean
|
||||
reserveSelection: Boolean,
|
||||
filterMethod: Function,
|
||||
filters: Array,
|
||||
filterMultiple: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
|
||||
render() {},
|
||||
|
@ -205,7 +206,13 @@ export default {
|
|||
formatter: this.formatter,
|
||||
selectable: this.selectable,
|
||||
reserveSelection: this.reserveSelection,
|
||||
fixed: this.fixed
|
||||
fixed: this.fixed,
|
||||
filterMethod: this.filterMethod,
|
||||
filters: this.filters,
|
||||
filterable: this.filters || this.filterMethod,
|
||||
filterMultiple: this.filterMultiple,
|
||||
filterOpened: false,
|
||||
filteredValue: []
|
||||
});
|
||||
|
||||
objectAssign(column, forced[type] || {});
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ElCheckbox from 'element-ui/packages/checkbox';
|
||||
import ElTag from 'element-ui/packages/tag';
|
||||
import Vue from 'vue';
|
||||
import FilterPanel from './filter-panel.vue';
|
||||
|
||||
export default {
|
||||
name: 'el-table-header',
|
||||
|
@ -31,21 +33,27 @@ export default {
|
|||
on-mousemove={ ($event) => this.handleMouseMove($event, column) }
|
||||
on-mouseout={ this.handleMouseOut }
|
||||
on-mousedown={ ($event) => this.handleMouseDown($event, column) }
|
||||
on-click={ ($event) => this.handleHeaderClick($event, column) }
|
||||
class={ [column.id, column.direction, column.align, this.isCellHidden(cellIndex) ? 'hidden' : ''] }>
|
||||
<div class={ ['cell', column.filteredValue && column.filteredValue.length > 0 ? 'highlight' : ''] }>
|
||||
{
|
||||
[
|
||||
column.headerTemplate
|
||||
? column.headerTemplate.call(this._renderProxy, h, column.label)
|
||||
: <div>{ column.label }</div>,
|
||||
column.sortable
|
||||
? <div class="caret-wrapper">
|
||||
<i class="sort-caret ascending"></i>
|
||||
<i class="sort-caret descending"></i>
|
||||
</div>
|
||||
: ''
|
||||
]
|
||||
column.headerTemplate
|
||||
? column.headerTemplate.call(this._renderProxy, h, column.label)
|
||||
: column.label
|
||||
}
|
||||
{
|
||||
column.sortable
|
||||
? <span class="caret-wrapper" on-click={ ($event) => this.handleHeaderClick($event, column) }>
|
||||
<i class="sort-caret ascending"></i>
|
||||
<i class="sort-caret descending"></i>
|
||||
</span>
|
||||
: ''
|
||||
}
|
||||
{
|
||||
column.filterable
|
||||
? <span class="el-table__column-filter-trigger" on-click={ ($event) => this.handleFilterClick($event, column) }><i class={ ['el-icon-arrow-down', column.filterOpened ? 'el-icon-arrow-up' : ''] }></i></span>
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
</th>
|
||||
)
|
||||
}
|
||||
|
@ -61,7 +69,6 @@ export default {
|
|||
},
|
||||
|
||||
props: {
|
||||
columns: {},
|
||||
fixed: String,
|
||||
store: {
|
||||
required: true
|
||||
|
@ -99,6 +106,19 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.filterPanels = {};
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
const panels = this.filterPanels;
|
||||
for (let prop in panels) {
|
||||
if (panels.hasOwnProperty(prop) && panels[prop]) {
|
||||
panels[prop].$destroy(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
isCellHidden(index) {
|
||||
if (this.fixed === true || this.fixed === 'left') {
|
||||
|
@ -114,6 +134,34 @@ export default {
|
|||
this.store.commit('toggleAllSelection');
|
||||
},
|
||||
|
||||
handleFilterClick(event, column) {
|
||||
event.stopPropagation();
|
||||
const target = event.target;
|
||||
const cell = target.parentNode;
|
||||
const table = this.$parent;
|
||||
|
||||
let filterPanel = this.filterPanels[column.id];
|
||||
|
||||
if (filterPanel && column.filterOpened) {
|
||||
filterPanel.showPopper = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!filterPanel) {
|
||||
filterPanel = new Vue(FilterPanel);
|
||||
this.filterPanels[column.id] = filterPanel;
|
||||
|
||||
filterPanel.table = table;
|
||||
filterPanel.cell = cell;
|
||||
filterPanel.column = column;
|
||||
filterPanel.$mount(document.createElement('div'));
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
filterPanel.showPopper = true;
|
||||
}, 16);
|
||||
},
|
||||
|
||||
handleMouseDown(event, column) {
|
||||
if (this.draggingColumn && this.border) {
|
||||
this.dragging = true;
|
||||
|
@ -180,7 +228,10 @@ export default {
|
|||
},
|
||||
|
||||
handleMouseMove(event, column) {
|
||||
const target = event.target;
|
||||
let target = event.target;
|
||||
while (target && target.tagName !== 'TH') {
|
||||
target = target.parentNode;
|
||||
}
|
||||
|
||||
if (!column || !column.resizable) return;
|
||||
|
||||
|
@ -194,7 +245,6 @@ export default {
|
|||
} else if (!this.dragging) {
|
||||
bodyStyle.cursor = '';
|
||||
this.draggingColumn = null;
|
||||
if (column.sortable) bodyStyle.cursor = 'pointer';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import debounce from 'throttle-debounce/debounce';
|
||||
import { orderBy } from './util';
|
||||
import { orderBy, getColumnById } from './util';
|
||||
|
||||
const getRowIdentity = (row, rowKey) => {
|
||||
if (!row) throw new Error('row is required when get row identity');
|
||||
|
@ -24,6 +24,7 @@ const TableStore = function(table, initialState = {}) {
|
|||
fixedColumns: [],
|
||||
rightFixedColumns: [],
|
||||
_data: null,
|
||||
filteredData: null,
|
||||
data: null,
|
||||
sortCondition: {
|
||||
column: null,
|
||||
|
@ -34,7 +35,8 @@ const TableStore = function(table, initialState = {}) {
|
|||
selection: [],
|
||||
reserveSelection: false,
|
||||
selectable: null,
|
||||
hoverRow: null
|
||||
hoverRow: null,
|
||||
filters: {}
|
||||
};
|
||||
|
||||
for (let prop in initialState) {
|
||||
|
@ -80,7 +82,38 @@ TableStore.prototype.mutations = {
|
|||
},
|
||||
|
||||
changeSortCondition(states) {
|
||||
states.data = orderBy((states._data || []), states.sortCondition.property, states.sortCondition.direction);
|
||||
states.data = orderBy((states.filteredData || states._data || []), states.sortCondition.property, states.sortCondition.direction);
|
||||
|
||||
Vue.nextTick(() => this.table.updateScrollY());
|
||||
},
|
||||
|
||||
filterChange(states, options) {
|
||||
let { column, values } = options;
|
||||
if (values && !Array.isArray(values)) {
|
||||
values = [values];
|
||||
}
|
||||
|
||||
const prop = column.property;
|
||||
if (prop) {
|
||||
states.filters[column.id] = values;
|
||||
}
|
||||
|
||||
let data = states._data;
|
||||
const filters = states.filters;
|
||||
|
||||
Object.keys(filters).forEach((columnId) => {
|
||||
const values = filters[columnId];
|
||||
if (!values || values.length === 0) return;
|
||||
const column = getColumnById(this.states, columnId);
|
||||
if (column && column.filterMethod) {
|
||||
data = data.filter((row) => {
|
||||
return values.some(value => column.filterMethod.call(null, value, row));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
states.filteredData = data;
|
||||
states.data = orderBy(data, states.sortCondition.property, states.sortCondition.direction);
|
||||
|
||||
Vue.nextTick(() => this.table.updateScrollY());
|
||||
},
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
import throttle from 'throttle-debounce/throttle';
|
||||
import debounce from 'throttle-debounce/debounce';
|
||||
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
|
||||
import { $t } from 'element-ui/src/locale';
|
||||
import TableStore from './table-store';
|
||||
import TableLayout from './table-layout';
|
||||
import TableBody from './table-body';
|
||||
|
@ -130,7 +131,7 @@
|
|||
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: '暂无数据'
|
||||
default: $t('el.table.emptyText')
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -75,3 +75,21 @@ export const orderBy = function(array, sortKey, reverse) {
|
|||
return a === b ? 0 : a > b ? order : -order;
|
||||
});
|
||||
};
|
||||
|
||||
export const getColumnById = function(table, columnId) {
|
||||
let column = null;
|
||||
table.columns.forEach(function(item) {
|
||||
if (item.id === columnId) {
|
||||
column = item;
|
||||
}
|
||||
});
|
||||
return column;
|
||||
};
|
||||
|
||||
export const getColumnByCell = function(table, cell) {
|
||||
const matches = (cell.className || '').match(/el-table_[^\s]+/gm);
|
||||
if (matches) {
|
||||
return getColumnById(table, matches[0]);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
@import "./loading.css";
|
||||
@import "./dialog.css";
|
||||
@import "./table.css";
|
||||
@import "./table-column.css";
|
||||
@import "./pagination.css";
|
||||
@import "./popover.css";
|
||||
@import "./tooltip.css";
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
@charset "UTF-8";
|
||||
@import "./checkbox.css";
|
||||
@import "./tag.css";
|
||||
@import "./common/var.css";
|
||||
|
||||
@component-namespace el {
|
||||
@b table-filter {
|
||||
border: solid 1px #d3dce6;
|
||||
border-radius: 2px;
|
||||
background-color: #fff;
|
||||
box-shadow: var(--dropdown-menu-box-shadow);
|
||||
box-sizing: border-box;
|
||||
margin: 2px 0;
|
||||
|
||||
/** used for dropdown mode */
|
||||
@e list {
|
||||
padding: 5px 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
@e list-item {
|
||||
line-height: 36px;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
font-size: var(--font-size-base);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--dropdown-menuItem-hover-fill);
|
||||
color: var(--dropdown-menuItem-hover-color);
|
||||
}
|
||||
|
||||
@when active {
|
||||
background-color: #20a0ff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
@e content {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
@e bottom {
|
||||
border-top: 1px solid #d3dce6;
|
||||
padding: 8px;
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #8492a6;
|
||||
cursor: pointer;
|
||||
font-size: var(--font-size-base);
|
||||
padding: 0 3px;
|
||||
|
||||
&:hover {
|
||||
color: #20a0ff;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
color: #c0ccda;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@e checkbox-group {
|
||||
padding: 10px;
|
||||
|
||||
.el-checkbox {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.el-checkbox:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -213,16 +213,21 @@
|
|||
vertical-align: middle;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.highlight {
|
||||
color: #20a0ff;
|
||||
}
|
||||
}
|
||||
|
||||
& div.caret-wrapper {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 10px;
|
||||
width: 10px;
|
||||
height: 12px;
|
||||
padding: 0;
|
||||
& .caret-wrapper {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-left: 5px;
|
||||
margin-top: -2px;
|
||||
width: 16px;
|
||||
height: 34px;
|
||||
overflow: initial;
|
||||
}
|
||||
|
||||
|
@ -233,19 +238,20 @@
|
|||
border: 0;
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
z-index: 2;
|
||||
|
||||
&.ascending {
|
||||
top: 0;
|
||||
top: 11px;
|
||||
border-top: none;
|
||||
border-right: 5px solid transparent;
|
||||
border-bottom: 5px solid #99A9BF;
|
||||
border-bottom: 5px solid #99a9bf;
|
||||
border-left: 5px solid transparent;
|
||||
}
|
||||
|
||||
&.descending {
|
||||
bottom: 0;
|
||||
border-top: 5px solid #99A9BF;
|
||||
bottom: 11px;
|
||||
border-top: 5px solid #99a9bf;
|
||||
border-right: 5px solid transparent;
|
||||
border-bottom: none;
|
||||
border-left: 5px solid transparent;
|
||||
|
@ -333,7 +339,12 @@
|
|||
z-index: -1;
|
||||
}
|
||||
|
||||
@e column-filter-label {
|
||||
@e column-filter-trigger {
|
||||
display: inline-block;
|
||||
line-height: 34px;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
|
||||
& i {
|
||||
color: #99a9bf;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,12 @@ export default {
|
|||
delete: 'Delete',
|
||||
preview: 'Preview',
|
||||
continue: 'Continue'
|
||||
},
|
||||
table: {
|
||||
emptyText: 'No Data',
|
||||
confirmFilter: 'Confirm',
|
||||
resetFilter: 'Reset',
|
||||
clearFilter: 'All'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -68,6 +68,12 @@ export default {
|
|||
delete: '删除',
|
||||
preview: '查看图片',
|
||||
continue: '继续上传'
|
||||
},
|
||||
table: {
|
||||
emptyText: '暂无数据',
|
||||
confirmFilter: '筛选',
|
||||
resetFilter: '重置',
|
||||
clearFilter: '全部'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -54,7 +54,7 @@ describe('Table', () => {
|
|||
});
|
||||
|
||||
it('row data', () => {
|
||||
const cells = toArray(vm.$el.querySelectorAll('.cell'))
|
||||
const cells = toArray(vm.$el.querySelectorAll('td .cell'))
|
||||
.map(node => node.textContent);
|
||||
|
||||
expect(cells).to.eql(testDataArr);
|
||||
|
@ -591,7 +591,7 @@ describe('Table', () => {
|
|||
it('ascending', done => {
|
||||
const elm = vm.$el.querySelector('.caret-wrapper');
|
||||
|
||||
elm.parentNode.click();
|
||||
elm.click();
|
||||
setTimeout(_ => {
|
||||
const lastCells = vm.$el.querySelectorAll('.el-table__body-wrapper tbody tr td:last-child');
|
||||
expect(toArray(lastCells).map(node => node.textContent))
|
||||
|
@ -603,7 +603,7 @@ describe('Table', () => {
|
|||
it('descending', done => {
|
||||
const elm = vm.$el.querySelector('.caret-wrapper');
|
||||
|
||||
elm.parentNode.click();
|
||||
elm.click();
|
||||
setTimeout(_ => {
|
||||
const lastCells = vm.$el.querySelectorAll('.el-table__body-wrapper tbody tr td:last-child');
|
||||
expect(toArray(lastCells).map(node => node.textContent))
|
||||
|
|
Loading…
Reference in New Issue