import Vue from 'vue';
|
import merge from 'element-ui/src/utils/merge';
|
import { getKeysMap, getRowIdentity, getColumnById, getColumnByKey, orderBy, toggleRowStatus } from '../util';
|
import expand from './expand';
|
import current from './current';
|
import tree from './tree';
|
|
const sortData = (data, states) => {
|
const sortingColumn = states.sortingColumn;
|
if (!sortingColumn || typeof sortingColumn.sortable === 'string') {
|
return data;
|
}
|
return orderBy(data, states.sortProp, states.sortOrder, sortingColumn.sortMethod, sortingColumn.sortBy);
|
};
|
|
const doFlattenColumns = (columns) => {
|
const result = [];
|
columns.forEach((column) => {
|
if (column.children) {
|
result.push.apply(result, doFlattenColumns(column.children));
|
} else {
|
result.push(column);
|
}
|
});
|
return result;
|
};
|
|
export default Vue.extend({
|
data() {
|
return {
|
states: {
|
// 3.0 版本后要求必须设置该属性
|
rowKey: null,
|
|
// 渲染的数据来源,是对 table 中的 data 过滤排序后的结果
|
data: [],
|
|
// 是否包含固定列
|
isComplex: false,
|
|
// 列
|
_columns: [], // 不可响应的
|
originColumns: [],
|
columns: [],
|
fixedColumns: [],
|
rightFixedColumns: [],
|
leafColumns: [],
|
fixedLeafColumns: [],
|
rightFixedLeafColumns: [],
|
leafColumnsLength: 0,
|
fixedLeafColumnsLength: 0,
|
rightFixedLeafColumnsLength: 0,
|
|
// 选择
|
isAllSelected: false,
|
selection: [],
|
reserveSelection: false,
|
selectOnIndeterminate: false,
|
selectable: null,
|
|
// 过滤
|
filters: {}, // 不可响应的
|
filteredData: null,
|
|
// 排序
|
sortingColumn: null,
|
sortProp: null,
|
sortOrder: null,
|
|
hoverRow: null
|
}
|
};
|
},
|
|
mixins: [expand, current, tree],
|
|
methods: {
|
// 检查 rowKey 是否存在
|
assertRowKey() {
|
const rowKey = this.states.rowKey;
|
if (!rowKey) throw new Error('[ElTable] prop row-key is required');
|
},
|
|
// 更新列
|
updateColumns() {
|
const states = this.states;
|
const _columns = states._columns || [];
|
states.fixedColumns = _columns.filter((column) => column.fixed === true || column.fixed === 'left');
|
states.rightFixedColumns = _columns.filter((column) => column.fixed === 'right');
|
|
if (states.fixedColumns.length > 0 && _columns[0] && _columns[0].type === 'selection' && !_columns[0].fixed) {
|
_columns[0].fixed = true;
|
states.fixedColumns.unshift(_columns[0]);
|
}
|
|
const notFixedColumns = _columns.filter(column => !column.fixed);
|
states.originColumns = [].concat(states.fixedColumns).concat(notFixedColumns).concat(states.rightFixedColumns);
|
|
const leafColumns = doFlattenColumns(notFixedColumns);
|
const fixedLeafColumns = doFlattenColumns(states.fixedColumns);
|
const rightFixedLeafColumns = doFlattenColumns(states.rightFixedColumns);
|
|
states.leafColumnsLength = leafColumns.length;
|
states.fixedLeafColumnsLength = fixedLeafColumns.length;
|
states.rightFixedLeafColumnsLength = rightFixedLeafColumns.length;
|
|
states.columns = [].concat(fixedLeafColumns).concat(leafColumns).concat(rightFixedLeafColumns);
|
states.isComplex = states.fixedColumns.length > 0 || states.rightFixedColumns.length > 0;
|
},
|
|
// 更新 DOM
|
scheduleLayout(needUpdateColumns) {
|
if (needUpdateColumns) {
|
this.updateColumns();
|
}
|
this.table.debouncedUpdateLayout();
|
},
|
|
// 选择
|
isSelected(row) {
|
const { selection = [] } = this.states;
|
return selection.indexOf(row) > -1;
|
},
|
|
clearSelection() {
|
const states = this.states;
|
states.isAllSelected = false;
|
const oldSelection = states.selection;
|
if (oldSelection.length) {
|
states.selection = [];
|
this.table.$emit('selection-change', []);
|
}
|
},
|
|
cleanSelection() {
|
const states = this.states;
|
const { data, rowKey, selection } = states;
|
let deleted;
|
if (rowKey) {
|
deleted = [];
|
const selectedMap = getKeysMap(selection, rowKey);
|
const dataMap = getKeysMap(data, rowKey);
|
for (let key in selectedMap) {
|
if (selectedMap.hasOwnProperty(key) && !dataMap[key]) {
|
deleted.push(selectedMap[key].row);
|
}
|
}
|
} else {
|
deleted = selection.filter(item => data.indexOf(item) === -1);
|
}
|
if (deleted.length) {
|
const newSelection = selection.filter(item => deleted.indexOf(item) === -1);
|
states.selection = newSelection;
|
this.table.$emit('selection-change', newSelection.slice());
|
}
|
},
|
|
toggleRowSelection(row, selected, emitChange = true) {
|
const changed = toggleRowStatus(this.states.selection, row, selected);
|
if (changed) {
|
const newSelection = (this.states.selection || []).slice();
|
// 调用 API 修改选中值,不触发 select 事件
|
if (emitChange) {
|
this.table.$emit('select', newSelection, row);
|
}
|
this.table.$emit('selection-change', newSelection);
|
}
|
},
|
|
_toggleAllSelection() {
|
const states = this.states;
|
const { data = [], selection } = states;
|
// when only some rows are selected (but not all), select or deselect all of them
|
// depending on the value of selectOnIndeterminate
|
const value = states.selectOnIndeterminate
|
? !states.isAllSelected
|
: !(states.isAllSelected || selection.length);
|
states.isAllSelected = value;
|
|
let selectionChanged = false;
|
data.forEach((row, index) => {
|
if (states.selectable) {
|
if (states.selectable.call(null, row, index) && toggleRowStatus(selection, row, value)) {
|
selectionChanged = true;
|
}
|
} else {
|
if (toggleRowStatus(selection, row, value)) {
|
selectionChanged = true;
|
}
|
}
|
});
|
|
if (selectionChanged) {
|
this.table.$emit('selection-change', selection ? selection.slice() : []);
|
}
|
this.table.$emit('select-all', selection);
|
},
|
|
updateSelectionByRowKey() {
|
const states = this.states;
|
const { selection, rowKey, data } = states;
|
const selectedMap = getKeysMap(selection, rowKey);
|
data.forEach(row => {
|
const rowId = getRowIdentity(row, rowKey);
|
const rowInfo = selectedMap[rowId];
|
if (rowInfo) {
|
selection[rowInfo.index] = row;
|
}
|
});
|
},
|
|
updateAllSelected() {
|
const states = this.states;
|
const { selection, rowKey, selectable } = states;
|
// data 为 null 时,解构时的默认值会被忽略
|
const data = states.data || [];
|
if (data.length === 0) {
|
states.isAllSelected = false;
|
return;
|
}
|
|
let selectedMap;
|
if (rowKey) {
|
selectedMap = getKeysMap(selection, rowKey);
|
}
|
const isSelected = function(row) {
|
if (selectedMap) {
|
return !!selectedMap[getRowIdentity(row, rowKey)];
|
} else {
|
return selection.indexOf(row) !== -1;
|
}
|
};
|
let isAllSelected = true;
|
let selectedCount = 0;
|
for (let i = 0, j = data.length; i < j; i++) {
|
const item = data[i];
|
const isRowSelectable = selectable && selectable.call(null, item, i);
|
if (!isSelected(item)) {
|
if (!selectable || isRowSelectable) {
|
isAllSelected = false;
|
break;
|
}
|
} else {
|
selectedCount++;
|
}
|
}
|
|
if (selectedCount === 0) isAllSelected = false;
|
states.isAllSelected = isAllSelected;
|
},
|
|
// 过滤与排序
|
updateFilters(columns, values) {
|
if (!Array.isArray(columns)) {
|
columns = [columns];
|
}
|
const states = this.states;
|
const filters = {};
|
columns.forEach(col => {
|
states.filters[col.id] = values;
|
filters[col.columnKey || col.id] = values;
|
});
|
|
return filters;
|
},
|
|
updateSort(column, prop, order) {
|
if (this.states.sortingColumn && this.states.sortingColumn !== column) {
|
this.states.sortingColumn.order = null;
|
}
|
this.states.sortingColumn = column;
|
this.states.sortProp = prop;
|
this.states.sortOrder = order;
|
},
|
|
execFilter() {
|
const states = this.states;
|
const { _data, filters } = states;
|
let data = _data;
|
|
Object.keys(filters).forEach((columnId) => {
|
const values = states.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, column));
|
});
|
}
|
});
|
|
states.filteredData = data;
|
},
|
|
execSort() {
|
const states = this.states;
|
states.data = sortData(states.filteredData, states);
|
},
|
|
// 根据 filters 与 sort 去过滤 data
|
execQuery(ignore) {
|
if (!(ignore && ignore.filter)) {
|
this.execFilter();
|
}
|
this.execSort();
|
},
|
|
clearFilter(columnKeys) {
|
const states = this.states;
|
const { tableHeader, fixedTableHeader, rightFixedTableHeader } = this.table.$refs;
|
|
let panels = {};
|
if (tableHeader) panels = merge(panels, tableHeader.filterPanels);
|
if (fixedTableHeader) panels = merge(panels, fixedTableHeader.filterPanels);
|
if (rightFixedTableHeader) panels = merge(panels, rightFixedTableHeader.filterPanels);
|
|
const keys = Object.keys(panels);
|
if (!keys.length) return;
|
|
if (typeof columnKeys === 'string') {
|
columnKeys = [columnKeys];
|
}
|
|
if (Array.isArray(columnKeys)) {
|
const columns = columnKeys.map(key => getColumnByKey(states, key));
|
keys.forEach(key => {
|
const column = columns.find(col => col.id === key);
|
if (column) {
|
// TODO: 优化这里的代码
|
panels[key].filteredValue = [];
|
}
|
});
|
this.commit('filterChange', {
|
column: columns,
|
values: [],
|
silent: true,
|
multi: true
|
});
|
} else {
|
keys.forEach(key => {
|
// TODO: 优化这里的代码
|
panels[key].filteredValue = [];
|
});
|
|
states.filters = {};
|
this.commit('filterChange', {
|
column: {},
|
values: [],
|
silent: true
|
});
|
}
|
},
|
|
clearSort() {
|
const states = this.states;
|
if (!states.sortingColumn) return;
|
|
this.updateSort(null, null, null);
|
this.commit('changeSortCondition', {
|
silent: true
|
});
|
},
|
|
// 适配层,expand-row-keys 在 Expand 与 TreeTable 中都有使用
|
setExpandRowKeysAdapter(val) {
|
// 这里会触发额外的计算,但为了兼容性,暂时这么做
|
this.setExpandRowKeys(val);
|
this.updateTreeExpandKeys(val);
|
},
|
|
// 展开行与 TreeTable 都要使用
|
toggleRowExpansionAdapter(row, expanded) {
|
const hasExpandColumn = this.states.columns.some(({ type }) => type === 'expand');
|
if (hasExpandColumn) {
|
this.toggleRowExpansion(row, expanded);
|
} else {
|
this.toggleTreeExpansion(row, expanded);
|
}
|
}
|
}
|
});
|