f1c1d65e79746750b03b2c9ff8f709aae0aa438fc9703f4cf8e3320c960722ae3587d4a9e02fd98e62f511830627f324ef49566c411cef0b433829465785fd 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. import Vue from 'vue';
  2. import merge from 'element-ui/src/utils/merge';
  3. import { getKeysMap, getRowIdentity, getColumnById, getColumnByKey, orderBy, toggleRowStatus } from '../util';
  4. import expand from './expand';
  5. import current from './current';
  6. import tree from './tree';
  7. const sortData = (data, states) => {
  8. const sortingColumn = states.sortingColumn;
  9. if (!sortingColumn || typeof sortingColumn.sortable === 'string') {
  10. return data;
  11. }
  12. return orderBy(data, states.sortProp, states.sortOrder, sortingColumn.sortMethod, sortingColumn.sortBy);
  13. };
  14. const doFlattenColumns = (columns) => {
  15. const result = [];
  16. columns.forEach((column) => {
  17. if (column.children) {
  18. result.push.apply(result, doFlattenColumns(column.children));
  19. } else {
  20. result.push(column);
  21. }
  22. });
  23. return result;
  24. };
  25. export default Vue.extend({
  26. data() {
  27. return {
  28. states: {
  29. // 3.0 版本后要求必须设置该属性
  30. rowKey: null,
  31. // 渲染的数据来源,是对 table 中的 data 过滤排序后的结果
  32. data: [],
  33. // 是否包含固定列
  34. isComplex: false,
  35. // 列
  36. _columns: [], // 不可响应的
  37. originColumns: [],
  38. columns: [],
  39. fixedColumns: [],
  40. rightFixedColumns: [],
  41. leafColumns: [],
  42. fixedLeafColumns: [],
  43. rightFixedLeafColumns: [],
  44. leafColumnsLength: 0,
  45. fixedLeafColumnsLength: 0,
  46. rightFixedLeafColumnsLength: 0,
  47. // 选择
  48. isAllSelected: false,
  49. selection: [],
  50. reserveSelection: false,
  51. selectOnIndeterminate: false,
  52. selectable: null,
  53. // 过滤
  54. filters: {}, // 不可响应的
  55. filteredData: null,
  56. // 排序
  57. sortingColumn: null,
  58. sortProp: null,
  59. sortOrder: null,
  60. hoverRow: null
  61. }
  62. };
  63. },
  64. mixins: [expand, current, tree],
  65. methods: {
  66. // 检查 rowKey 是否存在
  67. assertRowKey() {
  68. const rowKey = this.states.rowKey;
  69. if (!rowKey) throw new Error('[ElTable] prop row-key is required');
  70. },
  71. // 更新列
  72. updateColumns() {
  73. const states = this.states;
  74. const _columns = states._columns || [];
  75. states.fixedColumns = _columns.filter((column) => column.fixed === true || column.fixed === 'left');
  76. states.rightFixedColumns = _columns.filter((column) => column.fixed === 'right');
  77. if (states.fixedColumns.length > 0 && _columns[0] && _columns[0].type === 'selection' && !_columns[0].fixed) {
  78. _columns[0].fixed = true;
  79. states.fixedColumns.unshift(_columns[0]);
  80. }
  81. const notFixedColumns = _columns.filter(column => !column.fixed);
  82. states.originColumns = [].concat(states.fixedColumns).concat(notFixedColumns).concat(states.rightFixedColumns);
  83. const leafColumns = doFlattenColumns(notFixedColumns);
  84. const fixedLeafColumns = doFlattenColumns(states.fixedColumns);
  85. const rightFixedLeafColumns = doFlattenColumns(states.rightFixedColumns);
  86. states.leafColumnsLength = leafColumns.length;
  87. states.fixedLeafColumnsLength = fixedLeafColumns.length;
  88. states.rightFixedLeafColumnsLength = rightFixedLeafColumns.length;
  89. states.columns = [].concat(fixedLeafColumns).concat(leafColumns).concat(rightFixedLeafColumns);
  90. states.isComplex = states.fixedColumns.length > 0 || states.rightFixedColumns.length > 0;
  91. },
  92. // 更新 DOM
  93. scheduleLayout(needUpdateColumns) {
  94. if (needUpdateColumns) {
  95. this.updateColumns();
  96. }
  97. this.table.debouncedUpdateLayout();
  98. },
  99. // 选择
  100. isSelected(row) {
  101. const { selection = [] } = this.states;
  102. return selection.indexOf(row) > -1;
  103. },
  104. clearSelection() {
  105. const states = this.states;
  106. states.isAllSelected = false;
  107. const oldSelection = states.selection;
  108. if (oldSelection.length) {
  109. states.selection = [];
  110. this.table.$emit('selection-change', []);
  111. }
  112. },
  113. cleanSelection() {
  114. const states = this.states;
  115. const { data, rowKey, selection } = states;
  116. let deleted;
  117. if (rowKey) {
  118. deleted = [];
  119. const selectedMap = getKeysMap(selection, rowKey);
  120. const dataMap = getKeysMap(data, rowKey);
  121. for (let key in selectedMap) {
  122. if (selectedMap.hasOwnProperty(key) && !dataMap[key]) {
  123. deleted.push(selectedMap[key].row);
  124. }
  125. }
  126. } else {
  127. deleted = selection.filter(item => data.indexOf(item) === -1);
  128. }
  129. if (deleted.length) {
  130. const newSelection = selection.filter(item => deleted.indexOf(item) === -1);
  131. states.selection = newSelection;
  132. this.table.$emit('selection-change', newSelection.slice());
  133. }
  134. },
  135. toggleRowSelection(row, selected, emitChange = true) {
  136. const changed = toggleRowStatus(this.states.selection, row, selected);
  137. if (changed) {
  138. const newSelection = (this.states.selection || []).slice();
  139. // 调用 API 修改选中值,不触发 select 事件
  140. if (emitChange) {
  141. this.table.$emit('select', newSelection, row);
  142. }
  143. this.table.$emit('selection-change', newSelection);
  144. }
  145. },
  146. _toggleAllSelection() {
  147. const states = this.states;
  148. const { data = [], selection } = states;
  149. // when only some rows are selected (but not all), select or deselect all of them
  150. // depending on the value of selectOnIndeterminate
  151. const value = states.selectOnIndeterminate
  152. ? !states.isAllSelected
  153. : !(states.isAllSelected || selection.length);
  154. states.isAllSelected = value;
  155. let selectionChanged = false;
  156. data.forEach((row, index) => {
  157. if (states.selectable) {
  158. if (states.selectable.call(null, row, index) && toggleRowStatus(selection, row, value)) {
  159. selectionChanged = true;
  160. }
  161. } else {
  162. if (toggleRowStatus(selection, row, value)) {
  163. selectionChanged = true;
  164. }
  165. }
  166. });
  167. if (selectionChanged) {
  168. this.table.$emit('selection-change', selection ? selection.slice() : []);
  169. }
  170. this.table.$emit('select-all', selection);
  171. },
  172. updateSelectionByRowKey() {
  173. const states = this.states;
  174. const { selection, rowKey, data } = states;
  175. const selectedMap = getKeysMap(selection, rowKey);
  176. data.forEach(row => {
  177. const rowId = getRowIdentity(row, rowKey);
  178. const rowInfo = selectedMap[rowId];
  179. if (rowInfo) {
  180. selection[rowInfo.index] = row;
  181. }
  182. });
  183. },
  184. updateAllSelected() {
  185. const states = this.states;
  186. const { selection, rowKey, selectable } = states;
  187. // data 为 null 时,解构时的默认值会被忽略
  188. const data = states.data || [];
  189. if (data.length === 0) {
  190. states.isAllSelected = false;
  191. return;
  192. }
  193. let selectedMap;
  194. if (rowKey) {
  195. selectedMap = getKeysMap(selection, rowKey);
  196. }
  197. const isSelected = function(row) {
  198. if (selectedMap) {
  199. return !!selectedMap[getRowIdentity(row, rowKey)];
  200. } else {
  201. return selection.indexOf(row) !== -1;
  202. }
  203. };
  204. let isAllSelected = true;
  205. let selectedCount = 0;
  206. for (let i = 0, j = data.length; i < j; i++) {
  207. const item = data[i];
  208. const isRowSelectable = selectable && selectable.call(null, item, i);
  209. if (!isSelected(item)) {
  210. if (!selectable || isRowSelectable) {
  211. isAllSelected = false;
  212. break;
  213. }
  214. } else {
  215. selectedCount++;
  216. }
  217. }
  218. if (selectedCount === 0) isAllSelected = false;
  219. states.isAllSelected = isAllSelected;
  220. },
  221. // 过滤与排序
  222. updateFilters(columns, values) {
  223. if (!Array.isArray(columns)) {
  224. columns = [columns];
  225. }
  226. const states = this.states;
  227. const filters = {};
  228. columns.forEach(col => {
  229. states.filters[col.id] = values;
  230. filters[col.columnKey || col.id] = values;
  231. });
  232. return filters;
  233. },
  234. updateSort(column, prop, order) {
  235. if (this.states.sortingColumn && this.states.sortingColumn !== column) {
  236. this.states.sortingColumn.order = null;
  237. }
  238. this.states.sortingColumn = column;
  239. this.states.sortProp = prop;
  240. this.states.sortOrder = order;
  241. },
  242. execFilter() {
  243. const states = this.states;
  244. const { _data, filters } = states;
  245. let data = _data;
  246. Object.keys(filters).forEach((columnId) => {
  247. const values = states.filters[columnId];
  248. if (!values || values.length === 0) return;
  249. const column = getColumnById(this.states, columnId);
  250. if (column && column.filterMethod) {
  251. data = data.filter((row) => {
  252. return values.some(value => column.filterMethod.call(null, value, row, column));
  253. });
  254. }
  255. });
  256. states.filteredData = data;
  257. },
  258. execSort() {
  259. const states = this.states;
  260. states.data = sortData(states.filteredData, states);
  261. },
  262. // 根据 filters 与 sort 去过滤 data
  263. execQuery(ignore) {
  264. if (!(ignore && ignore.filter)) {
  265. this.execFilter();
  266. }
  267. this.execSort();
  268. },
  269. clearFilter(columnKeys) {
  270. const states = this.states;
  271. const { tableHeader, fixedTableHeader, rightFixedTableHeader } = this.table.$refs;
  272. let panels = {};
  273. if (tableHeader) panels = merge(panels, tableHeader.filterPanels);
  274. if (fixedTableHeader) panels = merge(panels, fixedTableHeader.filterPanels);
  275. if (rightFixedTableHeader) panels = merge(panels, rightFixedTableHeader.filterPanels);
  276. const keys = Object.keys(panels);
  277. if (!keys.length) return;
  278. if (typeof columnKeys === 'string') {
  279. columnKeys = [columnKeys];
  280. }
  281. if (Array.isArray(columnKeys)) {
  282. const columns = columnKeys.map(key => getColumnByKey(states, key));
  283. keys.forEach(key => {
  284. const column = columns.find(col => col.id === key);
  285. if (column) {
  286. // TODO: 优化这里的代码
  287. panels[key].filteredValue = [];
  288. }
  289. });
  290. this.commit('filterChange', {
  291. column: columns,
  292. values: [],
  293. silent: true,
  294. multi: true
  295. });
  296. } else {
  297. keys.forEach(key => {
  298. // TODO: 优化这里的代码
  299. panels[key].filteredValue = [];
  300. });
  301. states.filters = {};
  302. this.commit('filterChange', {
  303. column: {},
  304. values: [],
  305. silent: true
  306. });
  307. }
  308. },
  309. clearSort() {
  310. const states = this.states;
  311. if (!states.sortingColumn) return;
  312. this.updateSort(null, null, null);
  313. this.commit('changeSortCondition', {
  314. silent: true
  315. });
  316. },
  317. // 适配层,expand-row-keys 在 Expand 与 TreeTable 中都有使用
  318. setExpandRowKeysAdapter(val) {
  319. // 这里会触发额外的计算,但为了兼容性,暂时这么做
  320. this.setExpandRowKeys(val);
  321. this.updateTreeExpandKeys(val);
  322. },
  323. // 展开行与 TreeTable 都要使用
  324. toggleRowExpansionAdapter(row, expanded) {
  325. const hasExpandColumn = this.states.columns.some(({ type }) => type === 'expand');
  326. if (hasExpandColumn) {
  327. this.toggleRowExpansion(row, expanded);
  328. } else {
  329. this.toggleTreeExpansion(row, expanded);
  330. }
  331. }
  332. }
  333. });