table-column.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. import { cellStarts, cellForced, defaultRenderCell, treeCellPrefix } from './config';
  2. import { mergeOptions, parseWidth, parseMinWidth, compose } from './util';
  3. import ElCheckbox from 'element-ui/packages/checkbox';
  4. let columnIdSeed = 1;
  5. export default {
  6. name: 'ElTableColumn',
  7. props: {
  8. type: {
  9. type: String,
  10. default: 'default'
  11. },
  12. label: String,
  13. className: String,
  14. labelClassName: String,
  15. property: String,
  16. prop: String,
  17. width: {},
  18. minWidth: {},
  19. renderHeader: Function,
  20. sortable: {
  21. type: [Boolean, String],
  22. default: false
  23. },
  24. sortMethod: Function,
  25. sortBy: [String, Function, Array],
  26. resizable: {
  27. type: Boolean,
  28. default: true
  29. },
  30. columnKey: String,
  31. align: String,
  32. headerAlign: String,
  33. showTooltipWhenOverflow: Boolean,
  34. showOverflowTooltip: Boolean,
  35. fixed: [Boolean, String],
  36. formatter: Function,
  37. selectable: Function,
  38. reserveSelection: Boolean,
  39. filterMethod: Function,
  40. filteredValue: Array,
  41. filters: Array,
  42. filterPlacement: String,
  43. filterMultiple: {
  44. type: Boolean,
  45. default: true
  46. },
  47. index: [Number, Function],
  48. sortOrders: {
  49. type: Array,
  50. default() {
  51. return ['ascending', 'descending', null];
  52. },
  53. validator(val) {
  54. return val.every(order => ['ascending', 'descending', null].indexOf(order) > -1);
  55. }
  56. }
  57. },
  58. data() {
  59. return {
  60. isSubColumn: false,
  61. columns: []
  62. };
  63. },
  64. computed: {
  65. owner() {
  66. let parent = this.$parent;
  67. while (parent && !parent.tableId) {
  68. parent = parent.$parent;
  69. }
  70. return parent;
  71. },
  72. columnOrTableParent() {
  73. let parent = this.$parent;
  74. while (parent && !parent.tableId && !parent.columnId) {
  75. parent = parent.$parent;
  76. }
  77. return parent;
  78. },
  79. realWidth() {
  80. return parseWidth(this.width);
  81. },
  82. realMinWidth() {
  83. return parseMinWidth(this.minWidth);
  84. },
  85. realAlign() {
  86. return this.align ? 'is-' + this.align : null;
  87. },
  88. realHeaderAlign() {
  89. return this.headerAlign ? 'is-' + this.headerAlign : this.realAlign;
  90. }
  91. },
  92. methods: {
  93. getPropsData(...props) {
  94. return props.reduce((prev, cur) => {
  95. if (Array.isArray(cur)) {
  96. cur.forEach((key) => {
  97. prev[key] = this[key];
  98. });
  99. }
  100. return prev;
  101. }, {});
  102. },
  103. getColumnElIndex(children, child) {
  104. return [].indexOf.call(children, child);
  105. },
  106. setColumnWidth(column) {
  107. if (this.realWidth) {
  108. column.width = this.realWidth;
  109. }
  110. if (this.realMinWidth) {
  111. column.minWidth = this.realMinWidth;
  112. }
  113. if (!column.minWidth) {
  114. column.minWidth = 80;
  115. }
  116. column.realWidth = column.width === undefined ? column.minWidth : column.width;
  117. return column;
  118. },
  119. setColumnForcedProps(column) {
  120. // 对于特定类型的 column,某些属性不允许设置
  121. const type = column.type;
  122. const source = cellForced[type] || {};
  123. Object.keys(source).forEach(prop => {
  124. let value = source[prop];
  125. if (value !== undefined) {
  126. column[prop] = prop === 'className' ? `${column[prop]} ${value}` : value;
  127. }
  128. });
  129. return column;
  130. },
  131. setColumnRenders(column) {
  132. // renderHeader 属性不推荐使用。
  133. if (this.renderHeader) {
  134. console.warn('[Element Warn][TableColumn]Comparing to render-header, scoped-slot header is easier to use. We recommend users to use scoped-slot header.');
  135. } else if (column.type !== 'selection') {
  136. column.renderHeader = (h, scope) => {
  137. const renderHeader = this.$scopedSlots.header;
  138. return renderHeader ? renderHeader(scope) : column.label;
  139. };
  140. }
  141. let originRenderCell = column.renderCell;
  142. // TODO: 这里的实现调整
  143. if (column.type === 'expand') {
  144. // 对于展开行,renderCell 不允许配置的。在上一步中已经设置过,这里需要简单封装一下。
  145. column.renderCell = (h, data) => (<div class="cell">
  146. { originRenderCell(h, data) }
  147. </div>);
  148. this.owner.renderExpanded = (h, data) => {
  149. return this.$scopedSlots.default
  150. ? this.$scopedSlots.default(data)
  151. : this.$slots.default;
  152. };
  153. } else {
  154. originRenderCell = originRenderCell || defaultRenderCell;
  155. // 对 renderCell 进行包装
  156. column.renderCell = (h, data) => {
  157. let children = null;
  158. if (this.$scopedSlots.default) {
  159. children = this.$scopedSlots.default(data);
  160. } else {
  161. children = originRenderCell(h, data);
  162. }
  163. const prefix = treeCellPrefix(h, data);
  164. const props = {
  165. class: 'cell',
  166. style: {}
  167. };
  168. if (column.showOverflowTooltip) {
  169. props.class += ' el-tooltip';
  170. props.style = {width: (data.column.realWidth || data.column.width) - 1 + 'px'};
  171. }
  172. return (<div { ...props }>
  173. { prefix }
  174. { children }
  175. </div>);
  176. };
  177. }
  178. return column;
  179. },
  180. registerNormalWatchers() {
  181. const props = ['label', 'property', 'filters', 'filterMultiple', 'sortable', 'index', 'formatter', 'className', 'labelClassName', 'showOverflowTooltip'];
  182. // 一些属性具有别名
  183. const aliases = {
  184. prop: 'property',
  185. realAlign: 'align',
  186. realHeaderAlign: 'headerAlign',
  187. realWidth: 'width'
  188. };
  189. const allAliases = props.reduce((prev, cur) => {
  190. prev[cur] = cur;
  191. return prev;
  192. }, aliases);
  193. Object.keys(allAliases).forEach(key => {
  194. const columnKey = aliases[key];
  195. this.$watch(key, (newVal) => {
  196. this.columnConfig[columnKey] = newVal;
  197. });
  198. });
  199. },
  200. registerComplexWatchers() {
  201. const props = ['fixed'];
  202. const aliases = {
  203. realWidth: 'width',
  204. realMinWidth: 'minWidth'
  205. };
  206. const allAliases = props.reduce((prev, cur) => {
  207. prev[cur] = cur;
  208. return prev;
  209. }, aliases);
  210. Object.keys(allAliases).forEach(key => {
  211. const columnKey = aliases[key];
  212. this.$watch(key, (newVal) => {
  213. this.columnConfig[columnKey] = newVal;
  214. const updateColumns = columnKey === 'fixed';
  215. this.owner.store.scheduleLayout(updateColumns);
  216. });
  217. });
  218. }
  219. },
  220. components: {
  221. ElCheckbox
  222. },
  223. beforeCreate() {
  224. this.row = {};
  225. this.column = {};
  226. this.$index = 0;
  227. this.columnId = '';
  228. },
  229. created() {
  230. const parent = this.columnOrTableParent;
  231. this.isSubColumn = this.owner !== parent;
  232. this.columnId = (parent.tableId || parent.columnId) + '_column_' + columnIdSeed++;
  233. const type = this.type || 'default';
  234. const sortable = this.sortable === '' ? true : this.sortable;
  235. const defaults = {
  236. ...cellStarts[type],
  237. id: this.columnId,
  238. type: type,
  239. property: this.prop || this.property,
  240. align: this.realAlign,
  241. headerAlign: this.realHeaderAlign,
  242. showOverflowTooltip: this.showOverflowTooltip || this.showTooltipWhenOverflow,
  243. // filter 相关属性
  244. filterable: this.filters || this.filterMethod,
  245. filteredValue: [],
  246. filterPlacement: '',
  247. isColumnGroup: false,
  248. filterOpened: false,
  249. // sort 相关属性
  250. sortable: sortable,
  251. // index 列
  252. index: this.index
  253. };
  254. const basicProps = ['columnKey', 'label', 'className', 'labelClassName', 'type', 'renderHeader', 'formatter', 'fixed', 'resizable'];
  255. const sortProps = ['sortMethod', 'sortBy', 'sortOrders'];
  256. const selectProps = ['selectable', 'reserveSelection'];
  257. const filterProps = ['filterMethod', 'filters', 'filterMultiple', 'filterOpened', 'filteredValue', 'filterPlacement'];
  258. let column = this.getPropsData(basicProps, sortProps, selectProps, filterProps);
  259. column = mergeOptions(defaults, column);
  260. // 注意 compose 中函数执行的顺序是从右到左
  261. const chains = compose(this.setColumnRenders, this.setColumnWidth, this.setColumnForcedProps);
  262. column = chains(column);
  263. this.columnConfig = column;
  264. // 注册 watcher
  265. this.registerNormalWatchers();
  266. this.registerComplexWatchers();
  267. },
  268. mounted() {
  269. const owner = this.owner;
  270. const parent = this.columnOrTableParent;
  271. const children = this.isSubColumn ? parent.$el.children : parent.$refs.hiddenColumns.children;
  272. const columnIndex = this.getColumnElIndex(children, this.$el);
  273. owner.store.commit('insertColumn', this.columnConfig, columnIndex, this.isSubColumn ? parent.columnConfig : null);
  274. },
  275. destroyed() {
  276. if (!this.$parent) return;
  277. const parent = this.$parent;
  278. this.owner.store.commit('removeColumn', this.columnConfig, this.isSubColumn ? parent.columnConfig : null);
  279. },
  280. render(h) {
  281. // slots 也要渲染,需要计算合并表头
  282. return h('div', this.$slots.default);
  283. }
  284. };