import { cellStarts, cellForced, defaultRenderCell, treeCellPrefix } from './config'; import { mergeOptions, parseWidth, parseMinWidth, compose } from './util'; import ElCheckbox from 'element-ui/packages/checkbox'; let columnIdSeed = 1; export default { name: 'ElTableColumn', props: { type: { type: String, default: 'default' }, label: String, className: String, labelClassName: String, property: String, prop: String, width: {}, minWidth: {}, renderHeader: Function, sortable: { type: [Boolean, String], default: false }, sortMethod: Function, sortBy: [String, Function, Array], resizable: { type: Boolean, default: true }, columnKey: String, align: String, headerAlign: String, showTooltipWhenOverflow: Boolean, showOverflowTooltip: Boolean, fixed: [Boolean, String], formatter: Function, selectable: Function, reserveSelection: Boolean, filterMethod: Function, filteredValue: Array, filters: Array, filterPlacement: String, filterMultiple: { type: Boolean, default: true }, index: [Number, Function], sortOrders: { type: Array, default() { return ['ascending', 'descending', null]; }, validator(val) { return val.every(order => ['ascending', 'descending', null].indexOf(order) > -1); } } }, data() { return { isSubColumn: false, columns: [] }; }, computed: { owner() { let parent = this.$parent; while (parent && !parent.tableId) { parent = parent.$parent; } return parent; }, columnOrTableParent() { let parent = this.$parent; while (parent && !parent.tableId && !parent.columnId) { parent = parent.$parent; } return parent; }, realWidth() { return parseWidth(this.width); }, realMinWidth() { return parseMinWidth(this.minWidth); }, realAlign() { return this.align ? 'is-' + this.align : null; }, realHeaderAlign() { return this.headerAlign ? 'is-' + this.headerAlign : this.realAlign; } }, methods: { getPropsData(...props) { return props.reduce((prev, cur) => { if (Array.isArray(cur)) { cur.forEach((key) => { prev[key] = this[key]; }); } return prev; }, {}); }, getColumnElIndex(children, child) { return [].indexOf.call(children, child); }, setColumnWidth(column) { if (this.realWidth) { column.width = this.realWidth; } if (this.realMinWidth) { column.minWidth = this.realMinWidth; } if (!column.minWidth) { column.minWidth = 80; } column.realWidth = column.width === undefined ? column.minWidth : column.width; return column; }, setColumnForcedProps(column) { // 对于特定类型的 column,某些属性不允许设置 const type = column.type; const source = cellForced[type] || {}; Object.keys(source).forEach(prop => { let value = source[prop]; if (value !== undefined) { column[prop] = prop === 'className' ? `${column[prop]} ${value}` : value; } }); return column; }, setColumnRenders(column) { // renderHeader 属性不推荐使用。 if (this.renderHeader) { console.warn('[Element Warn][TableColumn]Comparing to render-header, scoped-slot header is easier to use. We recommend users to use scoped-slot header.'); } else if (column.type !== 'selection') { column.renderHeader = (h, scope) => { const renderHeader = this.$scopedSlots.header; return renderHeader ? renderHeader(scope) : column.label; }; } let originRenderCell = column.renderCell; // TODO: 这里的实现调整 if (column.type === 'expand') { // 对于展开行,renderCell 不允许配置的。在上一步中已经设置过,这里需要简单封装一下。 column.renderCell = (h, data) => (
{ originRenderCell(h, data) }
); this.owner.renderExpanded = (h, data) => { return this.$scopedSlots.default ? this.$scopedSlots.default(data) : this.$slots.default; }; } else { originRenderCell = originRenderCell || defaultRenderCell; // 对 renderCell 进行包装 column.renderCell = (h, data) => { let children = null; if (this.$scopedSlots.default) { children = this.$scopedSlots.default(data); } else { children = originRenderCell(h, data); } const prefix = treeCellPrefix(h, data); const props = { class: 'cell', style: {} }; if (column.showOverflowTooltip) { props.class += ' el-tooltip'; props.style = {width: (data.column.realWidth || data.column.width) - 1 + 'px'}; } return (
{ prefix } { children }
); }; } return column; }, registerNormalWatchers() { const props = ['label', 'property', 'filters', 'filterMultiple', 'sortable', 'index', 'formatter', 'className', 'labelClassName', 'showOverflowTooltip']; // 一些属性具有别名 const aliases = { prop: 'property', realAlign: 'align', realHeaderAlign: 'headerAlign', realWidth: 'width' }; const allAliases = props.reduce((prev, cur) => { prev[cur] = cur; return prev; }, aliases); Object.keys(allAliases).forEach(key => { const columnKey = aliases[key]; this.$watch(key, (newVal) => { this.columnConfig[columnKey] = newVal; }); }); }, registerComplexWatchers() { const props = ['fixed']; const aliases = { realWidth: 'width', realMinWidth: 'minWidth' }; const allAliases = props.reduce((prev, cur) => { prev[cur] = cur; return prev; }, aliases); Object.keys(allAliases).forEach(key => { const columnKey = aliases[key]; this.$watch(key, (newVal) => { this.columnConfig[columnKey] = newVal; const updateColumns = columnKey === 'fixed'; this.owner.store.scheduleLayout(updateColumns); }); }); } }, components: { ElCheckbox }, beforeCreate() { this.row = {}; this.column = {}; this.$index = 0; this.columnId = ''; }, created() { const parent = this.columnOrTableParent; this.isSubColumn = this.owner !== parent; this.columnId = (parent.tableId || parent.columnId) + '_column_' + columnIdSeed++; const type = this.type || 'default'; const sortable = this.sortable === '' ? true : this.sortable; const defaults = { ...cellStarts[type], id: this.columnId, type: type, property: this.prop || this.property, align: this.realAlign, headerAlign: this.realHeaderAlign, showOverflowTooltip: this.showOverflowTooltip || this.showTooltipWhenOverflow, // filter 相关属性 filterable: this.filters || this.filterMethod, filteredValue: [], filterPlacement: '', isColumnGroup: false, filterOpened: false, // sort 相关属性 sortable: sortable, // index 列 index: this.index }; const basicProps = ['columnKey', 'label', 'className', 'labelClassName', 'type', 'renderHeader', 'formatter', 'fixed', 'resizable']; const sortProps = ['sortMethod', 'sortBy', 'sortOrders']; const selectProps = ['selectable', 'reserveSelection']; const filterProps = ['filterMethod', 'filters', 'filterMultiple', 'filterOpened', 'filteredValue', 'filterPlacement']; let column = this.getPropsData(basicProps, sortProps, selectProps, filterProps); column = mergeOptions(defaults, column); // 注意 compose 中函数执行的顺序是从右到左 const chains = compose(this.setColumnRenders, this.setColumnWidth, this.setColumnForcedProps); column = chains(column); this.columnConfig = column; // 注册 watcher this.registerNormalWatchers(); this.registerComplexWatchers(); }, mounted() { const owner = this.owner; const parent = this.columnOrTableParent; const children = this.isSubColumn ? parent.$el.children : parent.$refs.hiddenColumns.children; const columnIndex = this.getColumnElIndex(children, this.$el); owner.store.commit('insertColumn', this.columnConfig, columnIndex, this.isSubColumn ? parent.columnConfig : null); }, destroyed() { if (!this.$parent) return; const parent = this.$parent; this.owner.store.commit('removeColumn', this.columnConfig, this.isSubColumn ? parent.columnConfig : null); }, render(h) { // slots 也要渲染,需要计算合并表头 return h('div', this.$slots.default); } };