基于element-ui,并参考 af-table-column 实现 自动列宽

160 阅读1分钟

直接上代码,下面是组件

继承所有 el-table 的功能和属性 在 mounted 钩子中自动计算列宽 通过分析表头和数据内容计算合适的列宽 支持动态数据更新后的重新计算

<template>
 <el-table
  ref="table"
  v-bind="$attrs"
  v-on="$listeners"
 >
  <slot />
  <template v-for="(_, slotName) in $scopedSlots" v-slot:[slotName]="scope">
   <slot :name="slotName" v-bind="scope" />
  </template>
 </el-table>
</template>

<script>
 import { Table } from 'element-ui'

 export default {
  name: 'AutoTable',
  inheritAttrs: false,
  components: {
   ElTable: Table
  },
  props: {
   autoFit: {
    type: Boolean,
    default: true
   },
   fontSize: {
    type: Number,
    default: 14
   },
   fontRate: {
    type: Object,
    default: () => ({
     CHAR_RATE: 1.2,  
     NUM_RATE: 0.6,   
     OTHER_RATE: 0.8  
    })
   }
  },
  mounted() {
   if (this.autoFit) {
    this.$nextTick(() => {
     this.autoFitColumns()
    })
   }
  },
  methods: {
   autoFitColumns() {
    const table = this.$refs.table
    if (!table || !table.data || table.data.length === 0) return
    const columns = this.getColumnsConfig()
    this.calculateColumnsWidth(columns, table.data)
   },

   getColumnsConfig() {
    return this.$slots.default
     .filter(vnode => vnode.componentOptions &&
      vnode.componentOptions.tag &&
      vnode.componentOptions.tag.endsWith('table-column'))
     .map(vnode => {
      const props = vnode.componentOptions.propsData || {}
      return {
       prop: props.prop,
       label: props.label,
       fixed: props.fixed,
       minWidth: props.minWidth,
       width: props.width,
       fontSize: props.fontSize || this.fontSize,
       fontRate: props.fontRate || this.fontRate
      }
     })
   },

   calculateColumnsWidth(columns, tableData) {
    columns.forEach(column => {
     if (column.width || !column.label) return
     const values = tableData.map(item => column.prop ? item[column.prop] : '')
     const minLength = this.getMaxLength(values, column.fontRate)
     const headerLength = column.label.length * column.fontRate.CHAR_RATE
     const maxLength = Math.max(minLength, headerLength)
     const finalWidth = maxLength * column.fontSize + 40 
     this.updateColumnWidth(column.prop, Math.ceil(finalWidth))
    })
   },

   getMaxLength(arr, fontRate) {
    return arr.reduce((length, item) => {
     if (item) {
      const str = item.toString()
      const char = str.match(/[\u2E80-\u9FFF]/g)
      const charLength = char ? char.length : 0
      const num = str.match(/\d|./g)
      const numLength = num ? num.length : 0
      const otherLength = str.length - charLength - numLength
      let newLength = charLength * fontRate.CHAR_RATE +
       numLength * fontRate.NUM_RATE +
       otherLength * fontRate.OTHER_RATE
      if (str.includes('\n')) {
       newLength = this.getMaxLength(str.split('\n'), fontRate)
      }
      if (length < newLength) {
       length = newLength
      }
     }
     return length
    }, 0)
   },

   updateColumnWidth(prop, width) {
    const columns = this.$refs.table.columns
    const column = columns.find(col => col.property === prop)
    if (column) {
     column.width = width
     column.realWidth = width
     this.$refs.table.doLayout()
    }
   }
  },
  watch: {
   '$attrs.data'(newVal) {
    if (this.autoFit && newVal && newVal.length > 0) {
     this.$nextTick(() => {
      this.autoFitColumns()
     })
    }
   }
  }
 }
</script>

使用示例:

<template>
 <div>
  <auto-table :data="tableData" ref="table">
       <el-table-column prop="date" label="日期"> </el-table-column>
       ......
  </auto-table>
 </div>
</template>